From 85e42e21790c591dd0020cbcc406a84c02597dbe Mon Sep 17 00:00:00 2001 From: Peerasak Unsakon Date: Tue, 6 Dec 2022 15:39:06 +0700 Subject: [PATCH 01/20] [UPDATE] Add `rawData` into `Data1` to `Data32` --- .../Statically Typed/ABIRawType+Static.swift | 321 ++++++++++++++---- 1 file changed, 257 insertions(+), 64 deletions(-) diff --git a/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift b/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift index f93281b9..4a274143 100644 --- a/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift +++ b/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift @@ -145,261 +145,454 @@ extension Data: ABIType { // When decoding it's easier to specify a type, instead of type + static size public protocol ABIStaticSizeDataType: ABIType {} -public struct Data1: ABIStaticSizeDataType { + public struct Data1: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(1) } public static var parser: ParserFunction = DataParser -} -public struct Data2: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data2: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(2) } public static var parser: ParserFunction = DataParser -} -public struct Data3: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data3: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(3) } public static var parser: ParserFunction = DataParser -} -public struct Data4: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data4: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(4) } public static var parser: ParserFunction = DataParser -} -public struct Data5: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data5: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(5) } public static var parser: ParserFunction = DataParser -} -public struct Data6: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data6: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(6) } public static var parser: ParserFunction = DataParser -} -public struct Data7: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data7: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(7) } public static var parser: ParserFunction = DataParser -} -public struct Data8: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data8: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(8) } public static var parser: ParserFunction = DataParser -} -public struct Data9: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data9: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(9) } public static var parser: ParserFunction = DataParser -} -public struct Data10: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data10: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(10) } public static var parser: ParserFunction = DataParser -} -public struct Data11: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data11: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(11) } public static var parser: ParserFunction = DataParser -} -public struct Data12: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data12: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(12) } public static var parser: ParserFunction = DataParser -} -public struct Data13: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data13: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(13) } public static var parser: ParserFunction = DataParser -} -public struct Data14: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data14: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(14) } public static var parser: ParserFunction = DataParser -} -public struct Data15: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data15: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(15) } public static var parser: ParserFunction = DataParser -} -public struct Data16: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data16: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(16) } public static var parser: ParserFunction = DataParser -} -public struct Data17: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data17: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(17) } public static var parser: ParserFunction = DataParser -} -public struct Data18: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data18: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(18) } public static var parser: ParserFunction = DataParser -} -public struct Data19: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data19: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(19) } public static var parser: ParserFunction = DataParser -} -public struct Data20: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data20: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(20) } public static var parser: ParserFunction = DataParser -} -public struct Data21: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data21: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(21) } public static var parser: ParserFunction = DataParser -} -public struct Data22: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data22: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(22) } public static var parser: ParserFunction = DataParser -} -public struct Data23: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data23: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(23) } public static var parser: ParserFunction = DataParser -} -public struct Data24: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data24: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(24) } public static var parser: ParserFunction = DataParser -} -public struct Data25: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data25: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(25) } public static var parser: ParserFunction = DataParser -} -public struct Data26: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data26: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(26) } public static var parser: ParserFunction = DataParser -} -public struct Data27: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data27: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(27) } public static var parser: ParserFunction = DataParser -} -public struct Data28: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data28: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(28) } public static var parser: ParserFunction = DataParser -} -public struct Data29: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data29: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(29) } public static var parser: ParserFunction = DataParser -} -public struct Data30: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data30: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(30) } public static var parser: ParserFunction = DataParser -} -public struct Data31: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data31: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(31) } public static var parser: ParserFunction = DataParser -} -public struct Data32: ABIStaticSizeDataType { + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + + public struct Data32: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(32) } public static var parser: ParserFunction = DataParser -} + + var rawData: Data + + public init(data: Data) { + self.rawData = data + } + } + public struct ABIArray: ABIType { let values: [T] From 56c51fa09d685767a91bfd32089368679e1d6719 Mon Sep 17 00:00:00 2001 From: Peerasak Unsakon Date: Tue, 6 Dec 2022 15:41:07 +0700 Subject: [PATCH 02/20] [UPDATE] Add case for static size data type to `ABIEncoder` --- .../Statically Typed/ABIEncoder+Static.swift | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/web3swift/src/Contract/Statically Typed/ABIEncoder+Static.swift b/web3swift/src/Contract/Statically Typed/ABIEncoder+Static.swift index 04b14801..7ea228bf 100644 --- a/web3swift/src/Contract/Statically Typed/ABIEncoder+Static.swift +++ b/web3swift/src/Contract/Statically Typed/ABIEncoder+Static.swift @@ -30,6 +30,70 @@ extension ABIEncoder { return try ABIEncoder.encodeRaw(String(value), forType: type, padded: !packed) case let value as UInt64: return try ABIEncoder.encodeRaw(String(value), forType: type, padded: !packed) + case let value as Data1: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data1.rawType, padded: !packed) + case let value as Data2: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data2.rawType, padded: !packed) + case let value as Data3: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data3.rawType, padded: !packed) + case let value as Data4: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data4.rawType, padded: !packed) + case let value as Data5: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data5.rawType, padded: !packed) + case let value as Data6: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data6.rawType, padded: !packed) + case let value as Data7: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data7.rawType, padded: !packed) + case let value as Data8: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data8.rawType, padded: !packed) + case let value as Data9: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data9.rawType, padded: !packed) + case let value as Data10: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data10.rawType, padded: !packed) + case let value as Data11: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data11.rawType, padded: !packed) + case let value as Data12: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data12.rawType, padded: !packed) + case let value as Data13: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data13.rawType, padded: !packed) + case let value as Data14: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data14.rawType, padded: !packed) + case let value as Data15: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data15.rawType, padded: !packed) + case let value as Data16: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data16.rawType, padded: !packed) + case let value as Data17: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data17.rawType, padded: !packed) + case let value as Data18: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data18.rawType, padded: !packed) + case let value as Data19: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data19.rawType, padded: !packed) + case let value as Data20: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data20.rawType, padded: !packed) + case let value as Data21: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data21.rawType, padded: !packed) + case let value as Data22: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data22.rawType, padded: !packed) + case let value as Data23: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data23.rawType, padded: !packed) + case let value as Data24: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data24.rawType, padded: !packed) + case let value as Data25: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data25.rawType, padded: !packed) + case let value as Data26: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data26.rawType, padded: !packed) + case let value as Data27: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data27.rawType, padded: !packed) + case let value as Data28: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data28.rawType, padded: !packed) + case let value as Data29: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data29.rawType, padded: !packed) + case let value as Data30: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data30.rawType, padded: !packed) + case let value as Data31: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data31.rawType, padded: !packed) + case let value as Data32: + return try ABIEncoder.encodeRaw(String(bytes: value.rawData.web3.bytes), forType: Data32.rawType, padded: !packed) case let data as Data: if let staticSize = staticSize { return try ABIEncoder.encodeRaw(String(bytes: data.web3.bytes), forType: .FixedBytes(staticSize), padded: !packed) From 0fecf0a1bcceb500c8c07de64e0db6d8ea02e8c0 Mon Sep 17 00:00:00 2001 From: Peerasak Unsakon Date: Tue, 6 Dec 2022 15:52:49 +0700 Subject: [PATCH 03/20] [UPDATE] Add more tests for ABIFunction encoder --- .../Contract/ABIFunctionEncoderTests.swift | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/web3sTests/Contract/ABIFunctionEncoderTests.swift b/web3sTests/Contract/ABIFunctionEncoderTests.swift index 9ef322e2..28b05d89 100644 --- a/web3sTests/Contract/ABIFunctionEncoderTests.swift +++ b/web3sTests/Contract/ABIFunctionEncoderTests.swift @@ -121,6 +121,18 @@ class ABIFunctionEncoderTests: XCTestCase { XCTAssertEqual(execData.web3.hexString, "0xaacaaf88000000000000000000000000fbbcfe69b3941682d87d6bec0b64099e00d78f8e00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c42df546f4000000000000000000000000fbbcfe69b3941682d87d6bec0b64099e00d78f8e000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000a78a7b5926c52c10f4b7e84d085f1999c5ec89ff000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041450298c35b4713c9722b9b771f67d004ece1d25cbd33534e5932ea88172d23e90600203208a3ef63195f75702b61da1715aa785784c75f7a92958800095081221c00000000000000000000000000000000000000000000000000000000000000") } + func testGivenArrayOfStaticSizeBytes_ThenEncodesCorrectly() { + let sigDatas = [ + Data(hex: "0x450298c35b4713c9722b9b771f67d004ece1d25cbd33534e5932ea88172d23e9")!, + Data(hex: "0x7efef35dcd300eec8819c4ce5cb6b57be685254d583954273c5cc16edee83790")! + ] + let signatures = sigDatas.map { Data32(data: $0) } + let exec = RelayerWithData32Execute(from: nil, gasPrice: nil, gasLimit: nil, signatures: signatures) + + let execData = try! exec.transaction().data! + XCTAssertEqual(execData.web3.hexString, "0x8af7c64900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002450298c35b4713c9722b9b771f67d004ece1d25cbd33534e5932ea88172d23e97efef35dcd300eec8819c4ce5cb6b57be685254d583954273c5cc16edee83790") + } + func testGivenArrayOfBigUInt_ThenEncodesCorrectly() { let values = [BigUInt(1), BigUInt(2), BigUInt(3)] @@ -421,6 +433,29 @@ private struct RelayerExecute: ABIFunction { } } +private struct RelayerWithData32Execute: ABIFunction { + static let name = "execute" + let contract = EthereumAddress.zero + let from: EthereumAddress? + var gasPrice: BigUInt? + var gasLimit: BigUInt? + + struct Response: ABIResponse { + static var types: [ABIType.Type] = [] + + init?(values: [ABIDecoder.DecodedValue]) throws { + + } + } + + let signatures: [Data32] + + func encode(to encoder: ABIFunctionEncoder) throws { + try encoder.encode(signatures) + } +} + + struct NumberTuple: ABITuple, Equatable { func encode(to encoder: ABIFunctionEncoder) throws { try encoder.encode(value) From 2401baa5951fad36cd423a64109de1bd700953dd Mon Sep 17 00:00:00 2001 From: Dionysis Karatzas Date: Tue, 6 Dec 2022 15:39:57 +0200 Subject: [PATCH 04/20] [ADD] SwiftFormat (#290) Summary: - Add Swiftformat configuration - Add script to run swiftformat - Reformat code - Run Swiftformat Lint on CI - Add a separate script prepareForPush.sh as more lints might come in future --- .github/workflows/CI.yml | 10 +- .gitignore | 2 + scripts/prepareForPush.sh | 7 + scripts/runSwiftFormat.sh | 63 ++ scripts/swiftformat.yml | 79 ++ .../EthereumAccount+SignTransaction.swift | 6 +- web3swift/src/Account/EthereumAccount.swift | 40 +- .../src/Account/EthereumKeyStorage.swift | 47 +- web3swift/src/Account/TypedData.swift | 56 +- web3swift/src/Client/BaseEthereumClient.swift | 30 +- .../src/Client/EthereumClient+Call.swift | 106 +-- .../src/Client/EthereumClientProtocol.swift | 102 +-- web3swift/src/Client/EthereumHttpClient.swift | 14 +- .../src/Client/EthereumWebSocketClient.swift | 364 +++++----- .../src/Client/Models/EthereumAddress.swift | 3 +- .../src/Client/Models/EthereumBlock.swift | 11 +- .../src/Client/Models/EthereumBlockInfo.swift | 5 +- web3swift/src/Client/Models/EthereumLog.swift | 24 +- .../src/Client/Models/EthereumNetwork.swift | 8 +- .../Client/Models/EthereumSubscription.swift | 2 +- .../Client/Models/EthereumSyncStatus.swift | 4 +- .../Client/Models/EthereumTransaction.swift | 6 +- .../Models/EthereumTransactionReceipt.swift | 21 +- .../EventLoopGroupProvider.swift | 12 +- .../HttpNetworkProvider.swift | 6 +- .../NetworkProviderProtocol.swift | 27 +- .../WebSocketConfiguration.swift | 66 +- .../WebSocketNetworkProvider.swift | 679 +++++++++--------- .../src/Client/RecursiveLogCollector.swift | 16 +- web3swift/src/Contract/ABIDecoder.swift | 68 +- web3swift/src/Contract/ABIEncoder.swift | 86 ++- web3swift/src/Contract/ABIRawType.swift | 52 +- .../Statically Typed/ABIDecoder+Static.swift | 51 +- .../Statically Typed/ABIEncoder+Static.swift | 16 +- .../ABIFunctionEncodable.swift | 2 +- .../Statically Typed/ABIFunctionEncoder.swift | 13 +- .../Statically Typed/ABIRawType+Static.swift | 33 +- .../EthereumClient+Static.swift | 134 ++-- web3swift/src/ENS/ENSContracts.swift | 32 +- web3swift/src/ENS/ENSMultiResolver.swift | 73 +- web3swift/src/ENS/ENSResolver.swift | 5 +- web3swift/src/ENS/EthereumNameService.swift | 132 ++-- web3swift/src/ERC1271/ERC1271.swift | 4 +- web3swift/src/ERC1271/ERC1271Functions.swift | 16 +- web3swift/src/ERC1271/ERC1271Responses.swift | 25 +- web3swift/src/ERC165/ERC165.swift | 18 +- web3swift/src/ERC20/ERC20.swift | 51 +- web3swift/src/ERC20/ERC20Events.swift | 2 +- web3swift/src/ERC20/ERC20Functions.swift | 106 +-- web3swift/src/ERC20/ERC20Responses.swift | 8 +- web3swift/src/ERC721/ERC721.swift | 139 ++-- web3swift/src/ERC721/ERC721Events.swift | 6 +- web3swift/src/ERC721/ERC721Functions.swift | 154 ++-- web3swift/src/ERC721/ERC721Responses.swift | 15 +- web3swift/src/Extensions/ByteExtensions.swift | 20 +- web3swift/src/Extensions/Data+Random.swift | 2 +- web3swift/src/Extensions/Extensions.swift | 2 +- web3swift/src/Extensions/HexExtensions.swift | 6 +- .../src/Extensions/ResultExtensions.swift | 10 +- web3swift/src/Extensions/String+Numeric.swift | 10 +- .../src/Extensions/URLSessionExtensions.swift | 2 +- web3swift/src/Multicall/Multicall.swift | 13 +- .../src/Multicall/MulticallContract.swift | 13 +- .../src/OffchainLookup/OffchainLookup.swift | 9 +- .../EthereumAccount+SignSIWERequest.swift | 4 +- web3swift/src/SIWE/SiweMessage+Codable.swift | 1 - web3swift/src/SIWE/SiweMessage+RegEx.swift | 11 +- web3swift/src/SIWE/SiweMessage+String.swift | 1 - .../src/SIWE/SiweMessage+Validation.swift | 30 +- web3swift/src/SIWE/SiweMessage.swift | 1 - web3swift/src/SIWE/SiweVerifier.swift | 17 +- web3swift/src/Utils/HexUtil.swift | 10 +- web3swift/src/Utils/KeyDerivation.swift | 83 +-- web3swift/src/Utils/KeyUtil.swift | 19 +- web3swift/src/Utils/KeystoreUtil.swift | 13 +- web3swift/src/Utils/PropertyWrappers.swift | 5 +- web3swift/src/Utils/RLP.swift | 33 +- 77 files changed, 1892 insertions(+), 1480 deletions(-) create mode 100755 scripts/prepareForPush.sh create mode 100755 scripts/runSwiftFormat.sh create mode 100644 scripts/swiftformat.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fd091596..fdd34813 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -5,7 +5,14 @@ on: branches: [ develop ] jobs: + lint: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - name: Lint + run: ./scripts/runSwiftFormat.sh -l macos: + needs: lint runs-on: macos-latest steps: - uses: actions/checkout@v3 @@ -14,6 +21,7 @@ jobs: - name: Tests run: swift test -v linux: + needs: lint runs-on: ubuntu-latest container: image: swift:5.5-bionic @@ -22,4 +30,4 @@ jobs: - name: Build run: swift build -v - name: Tests - run: swift test -v \ No newline at end of file + run: swift test -v diff --git a/.gitignore b/.gitignore index 2648fe61..38dc9aff 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,5 @@ fastlane/report.xml fastlane/Preview.html fastlane/screenshots fastlane/test_output + +scripts/bin/* diff --git a/scripts/prepareForPush.sh b/scripts/prepareForPush.sh new file mode 100755 index 00000000..5b2b2d38 --- /dev/null +++ b/scripts/prepareForPush.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +SWIFT_VERSION=5.3 + +cd "$(dirname "$0")" + +./runSwiftFormat.sh diff --git a/scripts/runSwiftFormat.sh b/scripts/runSwiftFormat.sh new file mode 100755 index 00000000..a3e0fbf3 --- /dev/null +++ b/scripts/runSwiftFormat.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +SWIFT_VERSION=5.3 + +cd "$(dirname "$0")" + +function downloadAndUnzip { + curl -L -o tool.zip $2 + unzip -o -d $1/ tool.zip + rm tool.zip +} + +if ! which bin/swiftformat >/dev/null; then + echo "warning: SwiftFormat not installed, installing..." + + mkdir -p -- "bin" + cd bin + rm -r ./* + + downloadAndUnzip "SwiftFormatTmp" "https://github.com/nicklockwood/SwiftFormat/releases/download/0.50.3/swiftformat.artifactbundle.zip" + mv -f ./SwiftFormatTmp/swiftformat.artifactbundle/swiftformat-0.50.3-macos/bin/swiftformat . + find . -name "*Tmp" -type d -prune -exec rm -rf '{}' + + for entry in ./* + do + chmod +x "$entry" + done + cd ../ +fi + +cleanup() { + exit_code=$? + if [[ ${exit_code} -eq 0 ]]; then + exit 0 + else + echo "Need to run scripts/prepareForPush.sh script to prepare the code before a PullRequest." + exit 1 + fi +} + +format() { + bin/swiftformat ../web3swift/src/ --config "swiftformat.yml" --swiftversion $SWIFT_VERSION + cleanup +} + +lint() { + bin/swiftformat --lint ../web3swift/src/ --config "swiftformat.yml" --swiftversion $SWIFT_VERSION + cleanup +} + +while getopts "fl" o; do + case "${o}" in + f) + format; + exit;; + l) + lint; + exit;; + *) + exit;; + esac +done + +format diff --git a/scripts/swiftformat.yml b/scripts/swiftformat.yml new file mode 100644 index 00000000..928456f5 --- /dev/null +++ b/scripts/swiftformat.yml @@ -0,0 +1,79 @@ +--acronyms ID,URL,UUID +--allman false +--assetliterals visual-width +--beforemarks +--binarygrouping 4,8 +--categorymark "MARK: %c" +--classthreshold 0 +--closingparen balanced +--closurevoid remove +--commas always +--conflictmarkers reject +--decimalgrouping 3,6 +--elseposition same-line +--emptybraces no-space +--enumnamespaces always +--enumthreshold 0 +--exponentcase lowercase +--exponentgrouping disabled +--extensionacl on-extension +--extensionlength 0 +--extensionmark "MARK: - %t + %c" +--fractiongrouping disabled +--fragment false +--funcattributes prev-line +--generictypes +--groupedextension "MARK: %c" +--guardelse same-line +--header ignore +--hexgrouping 4,8 +--hexliteralcase uppercase +--ifdef indent +--importgrouping length +--indent 4 +--indentcase false +--indentstrings false +--lifecycle +--lineaftermarks false +--linebreaks lf +--markcategories true +--markextensions always +--marktypes always +--maxwidth none +--modifierorder +--nevertrailing +--nospaceoperators +--nowrapoperators +--octalgrouping 4,8 +--operatorfunc spaced +--organizetypes actor,class,enum,struct +--patternlet hoist +--ranges spaced +--redundanttype infer-locals-only +--self remove +--selfrequired +--semicolons inline +--shortoptionals always +--smarttabs enabled +--someAny true +--stripunusedargs always +--structthreshold 0 +--tabwidth unspecified +--trailingclosures +--trimwhitespace always +--typeattributes prev-line +--typeblanklines remove +--typemark "MARK: - %t" +--varattributes same-line +--voidtype void +--wraparguments before-first +--wrapcollections before-first +--wrapconditions preserve +--wrapparameters before-first +--wrapreturntype preserve +--wrapternary default +--wraptypealiases preserve +--xcodeindentation disabled +--yodaswap always +--disable enumNamespaces,extensionAccessControl,fileHeader,genericExtensions,modifierOrder,numberFormatting,opaqueGenericParameters,preferKeyPath,redundantBackticks,redundantExtensionACL,redundantFileprivate,redundantPattern,redundantRawValues,redundantSelf,sortDeclarations,spaceAroundGenerics,strongOutlets,trailingClosures,trailingCommas,unusedArguments,wrap,wrapMultilineStatementBraces,wrapSingleLineComments,yodaConditions +--enable blankLineAfterImports,blankLinesBetweenImports,isEmpty,wrapConditionalBodies diff --git a/web3swift/src/Account/EthereumAccount+SignTransaction.swift b/web3swift/src/Account/EthereumAccount+SignTransaction.swift index df58bb78..5c47ec4a 100644 --- a/web3swift/src/Account/EthereumAccount+SignTransaction.swift +++ b/web3swift/src/Account/EthereumAccount+SignTransaction.swift @@ -11,7 +11,6 @@ enum EthereumSignerError: Error { } public extension EthereumAccount { - func signRaw(_ transaction: EthereumTransaction) throws -> Data { let signed: SignedTransaction = try sign(transaction: transaction) guard let raw = signed.raw else { @@ -21,7 +20,6 @@ public extension EthereumAccount { } func sign(transaction: EthereumTransaction) throws -> SignedTransaction { - guard let raw = transaction.raw else { throw EthereumSignerError.emptyRawTransaction } @@ -30,8 +28,8 @@ public extension EthereumAccount { throw EthereumSignerError.unknownError } - let r = signature.subdata(in: 0..<32) - let s = signature.subdata(in: 32..<64) + let r = signature.subdata(in: 0 ..< 32) + let s = signature.subdata(in: 32 ..< 64) var v = Int(signature[64]) if v < 37 { diff --git a/web3swift/src/Account/EthereumAccount.swift b/web3swift/src/Account/EthereumAccount.swift index c219a02e..2d36829f 100644 --- a/web3swift/src/Account/EthereumAccount.swift +++ b/web3swift/src/Account/EthereumAccount.swift @@ -3,8 +3,8 @@ // Copyright © 2022 Argent Labs Limited. All rights reserved. // -import Foundation import Logging +import Foundation public protocol EthereumAccountProtocol { var address: EthereumAddress { get } @@ -29,13 +29,9 @@ public class EthereumAccount: EthereumAccountProtocol { private let publicKeyData: Data private let logger: Logger - public lazy var publicKey: String = { - return self.publicKeyData.web3.hexString - }() + public lazy var publicKey: String = self.publicKeyData.web3.hexString - public lazy var address: EthereumAddress = { - return KeyUtil.generateAddress(from: self.publicKeyData) - }() + public lazy var address: EthereumAddress = KeyUtil.generateAddress(from: self.publicKeyData) required public init(keyStorage: EthereumSingleKeyStorageProtocol, keystorePassword password: String, logger: Logger? = nil) throws { self.logger = logger ?? Logger(label: "web3.swift.eth-account") @@ -43,7 +39,7 @@ public class EthereumAccount: EthereumAccountProtocol { let decodedKey = try keyStorage.loadAndDecryptPrivateKey(keystorePassword: password) self.privateKeyData = decodedKey self.publicKeyData = try KeyUtil.generatePublicKey(from: decodedKey) - } catch let error { + } catch { self.logger.warning("Error loading key data: \(error)") throw EthereumAccountError.loadAccountError } @@ -59,7 +55,7 @@ public class EthereumAccount: EthereumAccountProtocol { throw EthereumAccountError.loadAccountError } } - + required public init(addressString: String, keyStorage: EthereumMultipleKeyStorageProtocol, keystorePassword password: String, logger: Logger? = nil) throws { self.logger = logger ?? Logger(label: "web3.swift.eth-account") do { @@ -67,12 +63,12 @@ public class EthereumAccount: EthereumAccountProtocol { let decodedKey = try keyStorage.loadAndDecryptPrivateKey(for: address, keystorePassword: password) self.privateKeyData = decodedKey self.publicKeyData = try KeyUtil.generatePublicKey(from: decodedKey) - } catch let error { + } catch { self.logger.warning("Error loading key data: \(error)") throw EthereumAccountError.loadAccountError } } - + required public init(addressString: String, keyStorage: EthereumMultipleKeyStorageProtocol, logger: Logger? = nil) throws { self.logger = logger ?? Logger(label: "web3.swift.eth-account") do { @@ -84,12 +80,12 @@ public class EthereumAccount: EthereumAccountProtocol { throw EthereumAccountError.loadAccountError } } - + public static func create(addingTo keyStorage: EthereumMultipleKeyStorageProtocol, keystorePassword password: String) throws -> EthereumAccount { guard let privateKey = KeyUtil.generatePrivateKeyData() else { throw EthereumAccountError.createAccountError } - + do { try keyStorage.encryptAndStorePrivateKey(key: privateKey, keystorePassword: password) let publicKey = try KeyUtil.generatePublicKey(from: privateKey) @@ -99,7 +95,7 @@ public class EthereumAccount: EthereumAccountProtocol { throw EthereumAccountError.createAccountError } } - + public static func create(replacing keyStorage: EthereumSingleKeyStorageProtocol, keystorePassword password: String) throws -> EthereumAccount { guard let privateKey = KeyUtil.generatePrivateKeyData() else { throw EthereumAccountError.createAccountError @@ -112,7 +108,7 @@ public class EthereumAccount: EthereumAccountProtocol { throw EthereumAccountError.createAccountError } } - + public static func importAccount(addingTo keyStorage: EthereumMultipleKeyStorageProtocol, privateKey: String, keystorePassword password: String) throws -> EthereumAccount { guard let privateKey = privateKey.web3.hexData else { throw EthereumAccountError.importAccountError @@ -126,7 +122,7 @@ public class EthereumAccount: EthereumAccountProtocol { throw EthereumAccountError.importAccountError } } - + public static func importAccount(replacing keyStorage: EthereumSingleKeyStorageProtocol, privateKey: String, keystorePassword password: String) throws -> EthereumAccount { guard let privateKey = privateKey.web3.hexData else { throw EthereumAccountError.importAccountError @@ -138,13 +134,13 @@ public class EthereumAccount: EthereumAccountProtocol { throw EthereumAccountError.importAccountError } } - + public func sign(data: Data) throws -> Data { - return try KeyUtil.sign(message: data, with: privateKeyData, hashing: true) + try KeyUtil.sign(message: data, with: privateKeyData, hashing: true) } public func sign(hex: String) throws -> Data { - if let data = Data.init(hex: hex) { + if let data = Data(hex: hex) { return try KeyUtil.sign(message: data, with: privateKeyData, hashing: true) } else { throw EthereumAccountError.signError @@ -160,7 +156,7 @@ public class EthereumAccount: EthereumAccountProtocol { } public func sign(message: Data) throws -> Data { - return try KeyUtil.sign(message: message, with: privateKeyData, hashing: false) + try KeyUtil.sign(message: message, with: privateKeyData, hashing: false) } public func sign(message: String) throws -> Data { @@ -181,13 +177,11 @@ public class EthereumAccount: EthereumAccountProtocol { guard var signed = try? sign(message: hash) else { throw EthereumAccountError.signError - } // Check last char (v) guard var last = signed.popLast() else { throw EthereumAccountError.signError - } if last < 27 { @@ -203,13 +197,11 @@ public class EthereumAccount: EthereumAccountProtocol { guard var signed = try? sign(message: hash) else { throw EthereumAccountError.signError - } // Check last char (v) guard var last = signed.popLast() else { throw EthereumAccountError.signError - } if last < 27 { diff --git a/web3swift/src/Account/EthereumKeyStorage.swift b/web3swift/src/Account/EthereumKeyStorage.swift index c9fff6e7..420154bb 100644 --- a/web3swift/src/Account/EthereumKeyStorage.swift +++ b/web3swift/src/Account/EthereumKeyStorage.swift @@ -26,36 +26,37 @@ public enum EthereumKeyStorageError: Error { } public class EthereumKeyLocalStorage: EthereumSingleKeyStorageProtocol { - public init() {} - + private var address: String? private let localFileName = "ethereumkey" - + private var addressPath: String? { - guard let address = address else { return nil } + guard let address = address else { + return nil + } if let url = folderPath { return url.appendingPathComponent(address).path } return nil } - + private var folderPath: URL? { if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first { return url } return nil } - + private var localPath: String? { if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first { return url.appendingPathComponent(localFileName).path } return nil } - + private let fileManager = FileManager.default - + public func storePrivateKey(key: Data) throws { guard let localPath = localPath else { throw EthereumKeyStorageError.failedToSave @@ -67,7 +68,7 @@ public class EthereumKeyLocalStorage: EthereumSingleKeyStorageProtocol { throw EthereumKeyStorageError.failedToSave } } - + public func loadPrivateKey() throws -> Data { guard let localPath = localPath else { throw EthereumKeyStorageError.failedToLoad @@ -86,12 +87,12 @@ extension EthereumKeyLocalStorage: EthereumMultipleKeyStorageProtocol { guard let folderPath = folderPath else { throw EthereumKeyStorageError.failedToLoad } - + do { try fileManager.createDirectory(atPath: folderPath.relativePath, withIntermediateDirectories: true) let directoryContents = try fileManager.contentsOfDirectory(at: folderPath, includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants]) - - let adressStrings = directoryContents.filter { !$0.hasDirectoryPath}.map { $0.lastPathComponent }.filter { $0.web3.isAddress } + + let adressStrings = directoryContents.filter { !$0.hasDirectoryPath }.map { $0.lastPathComponent }.filter { $0.web3.isAddress } let ethereumAdresses = adressStrings.map { EthereumAddress($0) } return ethereumAdresses } catch { @@ -99,14 +100,14 @@ extension EthereumKeyLocalStorage: EthereumMultipleKeyStorageProtocol { throw EthereumKeyStorageError.failedToLoad } } - - public func storePrivateKey(key: Data, with address: EthereumAddress) throws -> Void { + + public func storePrivateKey(key: Data, with address: EthereumAddress) throws { self.address = address.value - + defer { self.address = nil } - + guard let localPath = self.addressPath else { throw EthereumKeyStorageError.failedToSave } @@ -117,14 +118,14 @@ extension EthereumKeyLocalStorage: EthereumMultipleKeyStorageProtocol { throw EthereumKeyStorageError.failedToSave } } - + public func loadPrivateKey(for address: EthereumAddress) throws -> Data { self.address = address.value - + defer { self.address = nil } - + guard let localPath = self.addressPath else { throw EthereumKeyStorageError.failedToLoad } @@ -132,15 +133,15 @@ extension EthereumKeyLocalStorage: EthereumMultipleKeyStorageProtocol { guard let data = NSKeyedUnarchiver.unarchiveObject(withFile: localPath) as? Data else { throw EthereumKeyStorageError.failedToLoad } - + return data } - + public func deleteAllKeys() throws { do { if let folderPath = folderPath { let directoryContents = try fileManager.contentsOfDirectory(atPath: folderPath.path) - let addresses = directoryContents.filter({ $0.web3.isAddress || $0 == localFileName}) + let addresses = directoryContents.filter({ $0.web3.isAddress || $0 == localFileName }) for address in addresses { try deletePrivateKey(for: EthereumAddress(address)) } @@ -150,7 +151,7 @@ extension EthereumKeyLocalStorage: EthereumMultipleKeyStorageProtocol { throw EthereumKeyStorageError.failedToDelete } } - + public func deletePrivateKey(for address: EthereumAddress) throws { do { if let folderPath = folderPath { diff --git a/web3swift/src/Account/TypedData.swift b/web3swift/src/Account/TypedData.swift index 4a908b82..4324bdba 100644 --- a/web3swift/src/Account/TypedData.swift +++ b/web3swift/src/Account/TypedData.swift @@ -12,8 +12,10 @@ public struct TypedVariable: Codable, Equatable { public var name: String public var type: String - public init(name: String, - type: String) { + public init( + name: String, + type: String + ) { self.name = name self.type = type } @@ -26,10 +28,12 @@ public struct TypedData: Codable, Equatable { public var domain: JSON public var message: JSON - public init(types: [String: [TypedVariable]], - primaryType: String, - domain: JSON, - message: JSON) { + public init( + types: [String: [TypedVariable]], + primaryType: String, + domain: JSON, + message: JSON + ) { self.types = types self.primaryType = primaryType self.domain = domain @@ -44,7 +48,7 @@ extension TypedData: CustomStringConvertible { guard let encoded = try? encoder.encode(message), let string = String(data: encoded, encoding: .utf8) else { - return "" + return "" } return string @@ -71,7 +75,7 @@ extension TypedData { let param = types[type]!.map { "\($0.type) \($0.name)" }.joined(separator: ",") return "\(type)(\(param))" }.joined() - + return encoded.data(using: .utf8) ?? Data() } @@ -84,24 +88,24 @@ extension TypedData { } let recursiveEncoded: [UInt8] = try valueTypes.flatMap { variable -> [UInt8] in - + // Decomposit the type if it is array type let components = variable.type.components(separatedBy: CharacterSet(charactersIn: "[]")) let parsedType = components[0] - + // Check the type is a custom type if types[parsedType] != nil { guard let json = data[variable.name] else { throw ABIError.invalidValue } - + // If is custom type array, recursively encode the array - if components.count == 3 && components[1].isEmpty { + if components.count == 3, components[1].isEmpty { let encoded = try json.arrayValue!.flatMap { try encodeData(data: $0, type: parsedType).web3.keccak256.web3.bytes } - + return Data(encoded).web3.keccak256.web3.bytes - } else if components.count == 3 && !components[1].isEmpty { - let num = String(components[1].filter { "0"..."9" ~= $0 }) + } else if components.count == 3, !components[1].isEmpty { + let num = String(components[1].filter { "0" ... "9" ~= $0 }) guard let int = Int(num), int == json.arrayValue?.count ?? 0 else { throw ABIError.invalidValue } @@ -109,7 +113,7 @@ extension TypedData { let encoded = try json.arrayValue!.flatMap { try encodeData(data: $0, type: parsedType) } return Data(encoded).web3.keccak256.web3.bytes } - + return try encodeData(data: json, type: variable.type).web3.keccak256.web3.bytes } else if let json = data[variable.name] { return try parseAtomicType(json, type: variable.type) @@ -117,7 +121,7 @@ extension TypedData { return [] } } - + encoded.append(contentsOf: recursiveEncoded) return Data(encoded) @@ -127,18 +131,18 @@ extension TypedData { // Decomposit the type if it is an array type let components = primaryType.components(separatedBy: CharacterSet(charactersIn: "[]")) let parsedType = components[0] - + return parsedType } - + private func findDependencies(primaryType: String, dependencies: Set = Set()) -> Set { var found = dependencies - + let parsedType = getParsedType(primaryType: primaryType) - + guard !found.contains(parsedType), - let primaryTypes = types[parsedType] else { - return found + let primaryTypes = types[parsedType] else { + return found } found.insert(parsedType) for type in primaryTypes { @@ -183,15 +187,15 @@ extension TypedData { } return try ABIEncoder.encode(BigUInt(value ? 1 : 0)).bytes - case .DynamicArray(let nested): + case let .DynamicArray(nested): guard let value = data.arrayValue else { throw ABIError.invalidValue } let encoded = try value.flatMap { try parseAtomicType($0, type: nested.rawValue) } - + return Data(encoded).web3.keccak256.web3.bytes - case .FixedArray(let nested, let count): + case let .FixedArray(nested, count): guard let value = data.arrayValue else { throw ABIError.invalidValue } diff --git a/web3swift/src/Client/BaseEthereumClient.swift b/web3swift/src/Client/BaseEthereumClient.swift index aebdcab4..7fb221db 100644 --- a/web3swift/src/Client/BaseEthereumClient.swift +++ b/web3swift/src/Client/BaseEthereumClient.swift @@ -4,11 +4,11 @@ // import BigInt -import Foundation import Logging +import Foundation #if canImport(FoundationNetworking) -import FoundationNetworking + import FoundationNetworking #endif public class BaseEthereumClient: EthereumClientProtocol { @@ -20,10 +20,12 @@ public class BaseEthereumClient: EthereumClientProtocol { public var network: EthereumNetwork? - init(networkProvider: NetworkProviderProtocol, - url: URL, - logger: Logger? = nil, - network: EthereumNetwork?) { + init( + networkProvider: NetworkProviderProtocol, + url: URL, + logger: Logger? = nil, + network: EthereumNetwork? + ) { self.url = url self.networkProvider = networkProvider self.logger = logger ?? Logger(label: "web3.swift.eth-client") @@ -157,10 +159,12 @@ public class BaseEthereumClient: EthereumClientProtocol { value = nil } - let params = CallParams(from: transaction.from?.value, - to: transaction.to.value, - value: value?.web3.hexStringNoLeadingZeroes, - data: transaction.data?.web3.hexString) + let params = CallParams( + from: transaction.from?.value, + to: transaction.to.value, + value: value?.web3.hexStringNoLeadingZeroes, + data: transaction.data?.web3.hexString + ) do { let data = try await networkProvider.send(method: "eth_estimateGas", params: params, receive: String.self) @@ -241,11 +245,11 @@ public class BaseEthereumClient: EthereumClientProtocol { } public func eth_getLogs(addresses: [EthereumAddress]?, topics: [String?]?, fromBlock from: EthereumBlock = .Earliest, toBlock to: EthereumBlock = .Latest) async throws -> [EthereumLog] { - return try await RecursiveLogCollector(ethClient: self).getAllLogs(addresses: addresses, topics: topics.map(Topics.plain), from: from, to: to) + try await RecursiveLogCollector(ethClient: self).getAllLogs(addresses: addresses, topics: topics.map(Topics.plain), from: from, to: to) } - public func eth_getLogs(addresses: [EthereumAddress]?, orTopics topics: [[String]?]?, fromBlock from: EthereumBlock = .Earliest, toBlock to: EthereumBlock = .Latest) async throws -> [EthereumLog] { - return try await RecursiveLogCollector(ethClient: self).getAllLogs(addresses: addresses, topics: topics.map(Topics.composed), from: from, to: to) + public func eth_getLogs(addresses: [EthereumAddress]?, orTopics topics: [[String]?]?, fromBlock from: EthereumBlock = .Earliest, toBlock to: EthereumBlock = .Latest) async throws -> [EthereumLog] { + try await RecursiveLogCollector(ethClient: self).getAllLogs(addresses: addresses, topics: topics.map(Topics.composed), from: from, to: to) } public func getLogs(addresses: [EthereumAddress]?, topics: Topics?, fromBlock: EthereumBlock, toBlock: EthereumBlock) async throws -> [EthereumLog] { diff --git a/web3swift/src/Client/EthereumClient+Call.swift b/web3swift/src/Client/EthereumClient+Call.swift index da6312a5..8f6fea4e 100644 --- a/web3swift/src/Client/EthereumClient+Call.swift +++ b/web3swift/src/Client/EthereumClient+Call.swift @@ -5,7 +5,7 @@ import Foundation #if canImport(FoundationNetworking) -import FoundationNetworking + import FoundationNetworking #endif public enum OffchainReadError: Error { @@ -17,14 +17,18 @@ public enum OffchainReadError: Error { } extension BaseEthereumClient { - public func eth_call(_ transaction: EthereumTransaction, - block: EthereumBlock = .Latest) async throws -> String { - return try await eth_call(transaction, resolution: .noOffchain(failOnExecutionError: true), block: block) + public func eth_call( + _ transaction: EthereumTransaction, + block: EthereumBlock = .Latest + ) async throws -> String { + try await eth_call(transaction, resolution: .noOffchain(failOnExecutionError: true), block: block) } - public func eth_call(_ transaction: EthereumTransaction, - resolution: CallResolution = .noOffchain(failOnExecutionError: true), - block: EthereumBlock = .Latest) async throws -> String { + public func eth_call( + _ transaction: EthereumTransaction, + resolution: CallResolution = .noOffchain(failOnExecutionError: true), + block: EthereumBlock = .Latest + ) async throws -> String { guard let transactionData = transaction.data else { throw EthereumClientError.noInputData } @@ -53,10 +57,12 @@ extension BaseEthereumClient { } } - let params = CallParams(from: transaction.from?.value, - to: transaction.to.value, - data: transactionData.web3.hexString, - block: block.stringValue) + let params = CallParams( + from: transaction.from?.value, + to: transaction.to.value, + data: transactionData.web3.hexString, + block: block.stringValue + ) do { let data = try await networkProvider.send(method: "eth_call", params: params, receive: String.self) @@ -70,14 +76,18 @@ extension BaseEthereumClient { switch resolution { case .noOffchain: throw EthereumClientError.executionError(result.error) - case .offchainAllowed(let redirects): + case let .offchainAllowed(redirects): if let lookup = result.offchainLookup, lookup.address == transaction.to { do { let data = try await self.offchainRead(lookup: lookup, maxReads: redirects) - return try await self.eth_call(.init(to: lookup.address, - data: lookup.encodeCall(withResponse: data)), - resolution: .noOffchain(failOnExecutionError: true), - block: block) + return try await self.eth_call( + .init( + to: lookup.address, + data: lookup.encodeCall(withResponse: data) + ), + resolution: .noOffchain(failOnExecutionError: true), + block: block + ) } catch { throw EthereumClientError.noResultFound } @@ -92,9 +102,11 @@ extension BaseEthereumClient { } // OffchainReadError - private func offchainRead(lookup: OffchainLookup, - attempt: Int = 1, - maxReads: Int = 4) async throws -> Data { + private func offchainRead( + lookup: OffchainLookup, + attempt: Int = 1, + maxReads: Int = 4 + ) async throws -> Data { guard !lookup.urls.isEmpty else { throw OffchainReadError.invalidResponse } @@ -102,11 +114,13 @@ extension BaseEthereumClient { let url = lookup.urls[0] do { - return try await offchainRead(sender: lookup.address, - data: lookup.callData, - rawURL: url, - attempt: attempt, - maxAttempts: maxReads) + return try await offchainRead( + sender: lookup.address, + data: lookup.callData, + rawURL: url, + attempt: attempt, + maxAttempts: maxReads + ) } catch { guard let error = error as? OffchainReadError, error.isNextURLAllowed else { throw error @@ -114,17 +128,21 @@ extension BaseEthereumClient { var lookup = lookup lookup.urls = Array(lookup.urls.dropFirst()) - return try await offchainRead(lookup: lookup, - attempt: attempt + 1, - maxReads: maxReads) + return try await offchainRead( + lookup: lookup, + attempt: attempt + 1, + maxReads: maxReads + ) } } - private func offchainRead(sender: EthereumAddress, - data: Data, - rawURL: String, - attempt: Int, - maxAttempts: Int) async throws -> Data { + private func offchainRead( + sender: EthereumAddress, + data: Data, + rawURL: String, + attempt: Int, + maxAttempts: Int + ) async throws -> Data { guard attempt <= maxAttempts else { throw OffchainReadError.tooManyRedirections } @@ -133,8 +151,7 @@ extension BaseEthereumClient { guard let url = URL(string: rawURL .replacingOccurrences(of: "{sender}", with: sender.value.lowercased()) - .replacingOccurrences(of: "{data}", with: data.web3.hexString.lowercased())) - else { + .replacingOccurrences(of: "{data}", with: data.web3.hexString.lowercased())) else { throw OffchainReadError.invalidParams } @@ -183,8 +200,7 @@ private struct OffchainReadJSONBody: Encodable { } private struct OffchainReadResponse: Decodable { - @DataStr - var data: Data + @DataStr var data: Data } private struct OffchainReadErrorResponse: Decodable { @@ -206,16 +222,20 @@ fileprivate extension OffchainReadError { } extension BaseEthereumClient { - public func eth_call(_ transaction: EthereumTransaction, - block: EthereumBlock = .Latest, - completionHandler: @escaping (Result) -> Void) { + public func eth_call( + _ transaction: EthereumTransaction, + block: EthereumBlock = .Latest, + completionHandler: @escaping (Result) -> Void + ) { eth_call(transaction, resolution: .noOffchain(failOnExecutionError: true), block: block, completionHandler: completionHandler) } - public func eth_call(_ transaction: EthereumTransaction, - resolution: CallResolution = .noOffchain(failOnExecutionError: true), - block: EthereumBlock = .Latest, - completionHandler: @escaping (Result) -> Void) { + public func eth_call( + _ transaction: EthereumTransaction, + resolution: CallResolution = .noOffchain(failOnExecutionError: true), + block: EthereumBlock = .Latest, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let result = try await eth_call(transaction, resolution: resolution, block: block) diff --git a/web3swift/src/Client/EthereumClientProtocol.swift b/web3swift/src/Client/EthereumClientProtocol.swift index cc4077ea..45a6e6e3 100644 --- a/web3swift/src/Client/EthereumClientProtocol.swift +++ b/web3swift/src/Client/EthereumClientProtocol.swift @@ -14,9 +14,9 @@ public enum CallResolution { public struct EquatableError: Error, Equatable { let base: Error - public static func ==(lhs: EquatableError, rhs: EquatableError) -> Bool { - return type(of: lhs.base) == type(of: rhs.base) && - lhs.base.localizedDescription == rhs.base.localizedDescription + public static func == (lhs: EquatableError, rhs: EquatableError) -> Bool { + type(of: lhs.base) == type(of: rhs.base) && + lhs.base.localizedDescription == rhs.base.localizedDescription } } @@ -35,28 +35,30 @@ public enum EthereumClientError: Error, Equatable { public protocol EthereumClientProtocol: AnyObject { var network: EthereumNetwork? { get } - func net_version(completionHandler: @escaping(Result) -> Void) - func eth_gasPrice(completionHandler: @escaping(Result) -> Void) - func eth_blockNumber(completionHandler: @escaping(Result) -> Void) - func eth_getBalance(address: EthereumAddress, block: EthereumBlock, completionHandler: @escaping(Result) -> Void) - func eth_getCode(address: EthereumAddress, block: EthereumBlock, completionHandler: @escaping(Result) -> Void) - func eth_estimateGas(_ transaction: EthereumTransaction, completionHandler: @escaping(Result) -> Void) - func eth_sendRawTransaction(_ transaction: EthereumTransaction, withAccount account: EthereumAccountProtocol, completionHandler: @escaping(Result) -> Void) - func eth_getTransactionCount(address: EthereumAddress, block: EthereumBlock, completionHandler: @escaping(Result) -> Void) - func eth_getTransaction(byHash txHash: String, completionHandler: @escaping(Result) -> Void) - func eth_getTransactionReceipt(txHash: String, completionHandler: @escaping(Result) -> Void) + func net_version(completionHandler: @escaping (Result) -> Void) + func eth_gasPrice(completionHandler: @escaping (Result) -> Void) + func eth_blockNumber(completionHandler: @escaping (Result) -> Void) + func eth_getBalance(address: EthereumAddress, block: EthereumBlock, completionHandler: @escaping (Result) -> Void) + func eth_getCode(address: EthereumAddress, block: EthereumBlock, completionHandler: @escaping (Result) -> Void) + func eth_estimateGas(_ transaction: EthereumTransaction, completionHandler: @escaping (Result) -> Void) + func eth_sendRawTransaction(_ transaction: EthereumTransaction, withAccount account: EthereumAccountProtocol, completionHandler: @escaping (Result) -> Void) + func eth_getTransactionCount(address: EthereumAddress, block: EthereumBlock, completionHandler: @escaping (Result) -> Void) + func eth_getTransaction(byHash txHash: String, completionHandler: @escaping (Result) -> Void) + func eth_getTransactionReceipt(txHash: String, completionHandler: @escaping (Result) -> Void) func eth_call( _ transaction: EthereumTransaction, block: EthereumBlock, - completionHandler: @escaping(Result) -> Void) + completionHandler: @escaping (Result) -> Void + ) func eth_call( _ transaction: EthereumTransaction, resolution: CallResolution, block: EthereumBlock, - completionHandler: @escaping(Result) -> Void) - func eth_getLogs(addresses: [EthereumAddress]?, topics: [String?]?, fromBlock: EthereumBlock, toBlock: EthereumBlock, completionHandler: @escaping(Result<[EthereumLog], EthereumClientError>) -> Void) - func eth_getLogs(addresses: [EthereumAddress]?, orTopics: [[String]?]?, fromBlock: EthereumBlock, toBlock: EthereumBlock, completionHandler: @escaping(Result<[EthereumLog], EthereumClientError>) -> Void) - func eth_getBlockByNumber(_ block: EthereumBlock, completionHandler: @escaping(Result) -> Void) + completionHandler: @escaping (Result) -> Void + ) + func eth_getLogs(addresses: [EthereumAddress]?, topics: [String?]?, fromBlock: EthereumBlock, toBlock: EthereumBlock, completionHandler: @escaping (Result<[EthereumLog], EthereumClientError>) -> Void) + func eth_getLogs(addresses: [EthereumAddress]?, orTopics: [[String]?]?, fromBlock: EthereumBlock, toBlock: EthereumBlock, completionHandler: @escaping (Result<[EthereumLog], EthereumClientError>) -> Void) + func eth_getBlockByNumber(_ block: EthereumBlock, completionHandler: @escaping (Result) -> Void) func getLogs(addresses: [EthereumAddress]?, topics: Topics?, fromBlock: EthereumBlock, toBlock: EthereumBlock) async throws -> [EthereumLog] // Async/Await @@ -79,53 +81,53 @@ public protocol EthereumClientProtocol: AnyObject { resolution: CallResolution, block: EthereumBlock ) async throws -> String - func eth_getLogs(addresses: [EthereumAddress]?, topics: [String?]?, fromBlock: EthereumBlock, toBlock: EthereumBlock) async throws -> [EthereumLog] - func eth_getLogs(addresses: [EthereumAddress]?, orTopics: [[String]?]?, fromBlock: EthereumBlock, toBlock: EthereumBlock) async throws -> [EthereumLog] + func eth_getLogs(addresses: [EthereumAddress]?, topics: [String?]?, fromBlock: EthereumBlock, toBlock: EthereumBlock) async throws -> [EthereumLog] + func eth_getLogs(addresses: [EthereumAddress]?, orTopics: [[String]?]?, fromBlock: EthereumBlock, toBlock: EthereumBlock) async throws -> [EthereumLog] func eth_getBlockByNumber(_ block: EthereumBlock) async throws -> EthereumBlockInfo } #if canImport(NIO) -import NIOWebSocket + import NIOWebSocket -public protocol EthereumClientWebSocketProtocol: EthereumClientProtocol { - var delegate: EthereumWebSocketClientDelegate? { get set } - var currentState: WebSocketState { get } + public protocol EthereumClientWebSocketProtocol: EthereumClientProtocol { + var delegate: EthereumWebSocketClientDelegate? { get set } + var currentState: WebSocketState { get } - func connect() - func disconnect(code: WebSocketErrorCode) - func refresh() + func connect() + func disconnect(code: WebSocketErrorCode) + func refresh() - func subscribe(type: EthereumSubscriptionType, completionHandler: @escaping(Result) -> Void) - func subscribe(type: EthereumSubscriptionType) async throws -> EthereumSubscription + func subscribe(type: EthereumSubscriptionType, completionHandler: @escaping (Result) -> Void) + func subscribe(type: EthereumSubscriptionType) async throws -> EthereumSubscription - func unsubscribe(_ subscription: EthereumSubscription, completionHandler: @escaping(Result) -> Void) - func unsubscribe(_ subscription: EthereumSubscription) async throws -> Bool + func unsubscribe(_ subscription: EthereumSubscription, completionHandler: @escaping (Result) -> Void) + func unsubscribe(_ subscription: EthereumSubscription) async throws -> Bool - func pendingTransactions(onSubscribe: @escaping(Result) -> Void, onData: @escaping(String) -> Void) - func pendingTransactions(onData: @escaping(String) -> Void) async throws -> EthereumSubscription + func pendingTransactions(onSubscribe: @escaping (Result) -> Void, onData: @escaping (String) -> Void) + func pendingTransactions(onData: @escaping (String) -> Void) async throws -> EthereumSubscription - func newBlockHeaders(onSubscribe: @escaping(Result) -> Void, onData: @escaping(EthereumHeader) -> Void) - func newBlockHeaders(onData: @escaping(EthereumHeader) -> Void) async throws -> EthereumSubscription + func newBlockHeaders(onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumHeader) -> Void) + func newBlockHeaders(onData: @escaping (EthereumHeader) -> Void) async throws -> EthereumSubscription - func syncing(onSubscribe: @escaping(Result) -> Void, onData: @escaping(EthereumSyncStatus) -> Void) - func syncing(onData: @escaping(EthereumSyncStatus) -> Void) async throws -> EthereumSubscription -} + func syncing(onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumSyncStatus) -> Void) + func syncing(onData: @escaping (EthereumSyncStatus) -> Void) async throws -> EthereumSubscription + } -public protocol EthereumWebSocketClientDelegate: AnyObject { - func onNewPendingTransaction(subscription: EthereumSubscription, txHash: String) - func onNewBlockHeader(subscription: EthereumSubscription, header: EthereumHeader) - func onSyncing(subscription: EthereumSubscription, sync: EthereumSyncStatus) - func onWebSocketReconnect() -} + public protocol EthereumWebSocketClientDelegate: AnyObject { + func onNewPendingTransaction(subscription: EthereumSubscription, txHash: String) + func onNewBlockHeader(subscription: EthereumSubscription, header: EthereumHeader) + func onSyncing(subscription: EthereumSubscription, sync: EthereumSyncStatus) + func onWebSocketReconnect() + } -extension EthereumWebSocketClientDelegate { - func onNewPendingTransaction(subscription: EthereumSubscription, txHash: String) {} + extension EthereumWebSocketClientDelegate { + func onNewPendingTransaction(subscription: EthereumSubscription, txHash: String) {} - func onNewBlockHeader(subscription: EthereumSubscription, header: EthereumHeader) {} + func onNewBlockHeader(subscription: EthereumSubscription, header: EthereumHeader) {} - func onSyncing(subscription: EthereumSubscription, sync: EthereumSyncStatus) {} + func onSyncing(subscription: EthereumSubscription, sync: EthereumSyncStatus) {} - func onWebSocketReconnect() {} -} + func onWebSocketReconnect() {} + } #endif diff --git a/web3swift/src/Client/EthereumHttpClient.swift b/web3swift/src/Client/EthereumHttpClient.swift index fb1eed2b..f2f3ba1a 100644 --- a/web3swift/src/Client/EthereumHttpClient.swift +++ b/web3swift/src/Client/EthereumHttpClient.swift @@ -3,20 +3,22 @@ // Copyright © 2022 Argent Labs Limited. All rights reserved. // -import Foundation import Logging +import Foundation #if canImport(FoundationNetworking) -import FoundationNetworking + import FoundationNetworking #endif public class EthereumHttpClient: BaseEthereumClient { let networkQueue: OperationQueue - public init(url: URL, - sessionConfig: URLSessionConfiguration = URLSession.shared.configuration, - logger: Logger? = nil, - network: EthereumNetwork? = nil) { + public init( + url: URL, + sessionConfig: URLSessionConfiguration = URLSession.shared.configuration, + logger: Logger? = nil, + network: EthereumNetwork? = nil + ) { let networkQueue = OperationQueue() networkQueue.name = "web3swift.client.networkQueue" networkQueue.maxConcurrentOperationCount = 4 diff --git a/web3swift/src/Client/EthereumWebSocketClient.swift b/web3swift/src/Client/EthereumWebSocketClient.swift index f8a5e08c..84c42ccf 100644 --- a/web3swift/src/Client/EthereumWebSocketClient.swift +++ b/web3swift/src/Client/EthereumWebSocketClient.swift @@ -4,226 +4,230 @@ // #if canImport(NIO) -import BigInt -import Foundation -import GenericJSON -import Logging -import NIO -import NIOCore -import NIOSSL -import NIOWebSocket - -#if canImport(FoundationNetworking) -import FoundationNetworking -#endif - -public enum WebSocketState { - case connecting - case open - case closed -} - -public class EthereumWebSocketClient: BaseEthereumClient { - public var delegate: EthereumWebSocketClientDelegate? { - get { - return provider.delegate - } - set { - provider.delegate = newValue + import NIO + import BigInt + import NIOSSL + import Logging + import NIOCore + import Foundation + import GenericJSON + import NIOWebSocket + + #if canImport(FoundationNetworking) + import FoundationNetworking + #endif + + public enum WebSocketState { + case connecting + case open + case closed + } + + public class EthereumWebSocketClient: BaseEthereumClient { + public var delegate: EthereumWebSocketClientDelegate? { + get { + provider.delegate + } + set { + provider.delegate = newValue + } } - } - public var onReconnectCallback: (() -> Void)? { - get { - return provider.onReconnectCallback - } - set { - provider.onReconnectCallback = newValue + public var onReconnectCallback: (() -> Void)? { + get { + provider.onReconnectCallback + } + set { + provider.onReconnectCallback = newValue + } } - } - - public var currentState: WebSocketState { - return provider.currentState - } - - private let networkQueue: OperationQueue - - private var provider: WebSocketNetworkProviderProtocol - - public init(url: URL, - eventLoopGroupProvider: EventLoopGroupProvider = .createNew, - configuration: WebSocketConfiguration = .init(), - sessionConfig: URLSessionConfiguration = URLSession.shared.configuration, - logger: Logger? = nil, - network: EthereumNetwork? = nil) { - let networkQueue = OperationQueue() - networkQueue.name = "web3swift.client.networkQueue" - networkQueue.maxConcurrentOperationCount = 4 - self.networkQueue = networkQueue - - let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: networkQueue) - - let provider = WebSocketNetworkProvider(url: url.absoluteString, - eventLoopGroupProvider: eventLoopGroupProvider, - configuration: configuration, - session: session, - logger: logger) - self.provider = provider - super.init(networkProvider: provider, url: url, logger: logger, network: network) - } - public func connect() { - provider.connect() - } - - public func disconnect(code: WebSocketErrorCode = .goingAway) { - provider.disconnect(code: code) - } + public var currentState: WebSocketState { + provider.currentState + } - /// Additional public API method to refresh the connection if still open (close, re-open). - /// For example, if the app suspects bad data / missed heart beats, it can try to refresh. - public func refresh() { - provider.refresh() - } -} - -extension EthereumWebSocketClient: EthereumClientWebSocketProtocol { - public func subscribe(type: EthereumSubscriptionType) async throws -> EthereumSubscription { - do { - let data = try await networkProvider.send(method: "eth_subscribe", params: [type.method, type.params].compactMap { $0 }, receive: String.self) - if let resDataString = data as? String { - let subscription = EthereumSubscription(type: type, id: resDataString) - provider.addSubscription(subscription, callback: { _ in }) - return subscription - } else { - throw EthereumClientError.unexpectedReturnValue - } - } catch { - throw failureHandler(error) + private let networkQueue: OperationQueue + + private var provider: WebSocketNetworkProviderProtocol + + public init( + url: URL, + eventLoopGroupProvider: EventLoopGroupProvider = .createNew, + configuration: WebSocketConfiguration = .init(), + sessionConfig: URLSessionConfiguration = URLSession.shared.configuration, + logger: Logger? = nil, + network: EthereumNetwork? = nil + ) { + let networkQueue = OperationQueue() + networkQueue.name = "web3swift.client.networkQueue" + networkQueue.maxConcurrentOperationCount = 4 + self.networkQueue = networkQueue + + let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: networkQueue) + + let provider = WebSocketNetworkProvider( + url: url.absoluteString, + eventLoopGroupProvider: eventLoopGroupProvider, + configuration: configuration, + session: session, + logger: logger + ) + self.provider = provider + super.init(networkProvider: provider, url: url, logger: logger, network: network) } - } - public func unsubscribe(_ subscription: EthereumSubscription) async throws -> Bool { - do { - let data = try await networkProvider.send(method: "eth_unsubscribe", params: [subscription.id], receive: Bool.self) - if let resDataBool = data as? Bool { - provider.removeSubscription(subscription) - return resDataBool - } else { - throw EthereumClientError.unexpectedReturnValue - } - } catch { - throw failureHandler(error) + public func connect() { + provider.connect() } - } - public func pendingTransactions(onData: @escaping (String) -> Void) async throws -> EthereumSubscription { - do { - let data = try await networkProvider.send(method: "eth_subscribe", params: [EthereumSubscriptionType.pendingTransactions.method], receive: String.self) - if let resDataString = data as? String { - let subscription = EthereumSubscription(type: .pendingTransactions, id: resDataString) - provider.addSubscription(subscription, callback: { object in - onData(object as! String) - }) - return subscription - } else { - throw EthereumClientError.unexpectedReturnValue - } - } catch { - throw failureHandler(error) + public func disconnect(code: WebSocketErrorCode = .goingAway) { + provider.disconnect(code: code) } - } - public func newBlockHeaders(onData: @escaping (EthereumHeader) -> Void) async throws -> EthereumSubscription { - do { - let data = try await networkProvider.send(method: "eth_subscribe", params: [EthereumSubscriptionType.newBlockHeaders.method], receive: String.self) - if let resDataString = data as? String { - let subscription = EthereumSubscription(type: .newBlockHeaders, id: resDataString) - provider.addSubscription(subscription, callback: { object in - onData(object as! EthereumHeader) - }) - return subscription - } else { - throw EthereumClientError.unexpectedReturnValue - } - } catch { - throw failureHandler(error) + /// Additional public API method to refresh the connection if still open (close, re-open). + /// For example, if the app suspects bad data / missed heart beats, it can try to refresh. + public func refresh() { + provider.refresh() } } - public func syncing(onData: @escaping (EthereumSyncStatus) -> Void) async throws -> EthereumSubscription { - do { - let data = try await networkProvider.send(method: "eth_subscribe", params: [EthereumSubscriptionType.syncing.method], receive: String.self) - if let resDataString = data as? String { - let subscription = EthereumSubscription(type: .syncing, id: resDataString) - provider.addSubscription(subscription, callback: { object in - onData(object as! EthereumSyncStatus) - }) - return subscription - } else { - throw EthereumClientError.unexpectedReturnValue + extension EthereumWebSocketClient: EthereumClientWebSocketProtocol { + public func subscribe(type: EthereumSubscriptionType) async throws -> EthereumSubscription { + do { + let data = try await networkProvider.send(method: "eth_subscribe", params: [type.method, type.params].compactMap { $0 }, receive: String.self) + if let resDataString = data as? String { + let subscription = EthereumSubscription(type: type, id: resDataString) + provider.addSubscription(subscription, callback: { _ in }) + return subscription + } else { + throw EthereumClientError.unexpectedReturnValue + } + } catch { + throw failureHandler(error) } - } catch { - throw failureHandler(error) } - } -} -extension EthereumWebSocketClient { - public func subscribe(type: EthereumSubscriptionType, completionHandler: @escaping (Result) -> Void) { - Task { + public func unsubscribe(_ subscription: EthereumSubscription) async throws -> Bool { do { - let result = try await subscribe(type: type) - completionHandler(.success(result)) + let data = try await networkProvider.send(method: "eth_unsubscribe", params: [subscription.id], receive: Bool.self) + if let resDataBool = data as? Bool { + provider.removeSubscription(subscription) + return resDataBool + } else { + throw EthereumClientError.unexpectedReturnValue + } } catch { - failureHandler(error, completionHandler: completionHandler) + throw failureHandler(error) } } - } - public func unsubscribe(_ subscription: EthereumSubscription, completionHandler: @escaping (Result) -> Void) { - Task { + public func pendingTransactions(onData: @escaping (String) -> Void) async throws -> EthereumSubscription { do { - let result = try await unsubscribe(subscription) - completionHandler(.success(result)) + let data = try await networkProvider.send(method: "eth_subscribe", params: [EthereumSubscriptionType.pendingTransactions.method], receive: String.self) + if let resDataString = data as? String { + let subscription = EthereumSubscription(type: .pendingTransactions, id: resDataString) + provider.addSubscription(subscription, callback: { object in + onData(object as! String) + }) + return subscription + } else { + throw EthereumClientError.unexpectedReturnValue + } } catch { - failureHandler(error, completionHandler: completionHandler) + throw failureHandler(error) } } - } - public func pendingTransactions(onSubscribe: @escaping (Result) -> Void, onData: @escaping (String) -> Void) { - Task { + public func newBlockHeaders(onData: @escaping (EthereumHeader) -> Void) async throws -> EthereumSubscription { do { - let result = try await pendingTransactions(onData: onData) - onSubscribe(.success(result)) + let data = try await networkProvider.send(method: "eth_subscribe", params: [EthereumSubscriptionType.newBlockHeaders.method], receive: String.self) + if let resDataString = data as? String { + let subscription = EthereumSubscription(type: .newBlockHeaders, id: resDataString) + provider.addSubscription(subscription, callback: { object in + onData(object as! EthereumHeader) + }) + return subscription + } else { + throw EthereumClientError.unexpectedReturnValue + } } catch { - failureHandler(error, completionHandler: onSubscribe) + throw failureHandler(error) } } - } - public func newBlockHeaders(onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumHeader) -> Void) { - Task { + public func syncing(onData: @escaping (EthereumSyncStatus) -> Void) async throws -> EthereumSubscription { do { - let result = try await newBlockHeaders(onData: onData) - onSubscribe(.success(result)) + let data = try await networkProvider.send(method: "eth_subscribe", params: [EthereumSubscriptionType.syncing.method], receive: String.self) + if let resDataString = data as? String { + let subscription = EthereumSubscription(type: .syncing, id: resDataString) + provider.addSubscription(subscription, callback: { object in + onData(object as! EthereumSyncStatus) + }) + return subscription + } else { + throw EthereumClientError.unexpectedReturnValue + } } catch { - failureHandler(error, completionHandler: onSubscribe) + throw failureHandler(error) } } } - public func syncing(onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumSyncStatus) -> Void) { - Task { - do { - let result = try await syncing(onData: onData) - onSubscribe(.success(result)) - } catch { - failureHandler(error, completionHandler: onSubscribe) + extension EthereumWebSocketClient { + public func subscribe(type: EthereumSubscriptionType, completionHandler: @escaping (Result) -> Void) { + Task { + do { + let result = try await subscribe(type: type) + completionHandler(.success(result)) + } catch { + failureHandler(error, completionHandler: completionHandler) + } + } + } + + public func unsubscribe(_ subscription: EthereumSubscription, completionHandler: @escaping (Result) -> Void) { + Task { + do { + let result = try await unsubscribe(subscription) + completionHandler(.success(result)) + } catch { + failureHandler(error, completionHandler: completionHandler) + } + } + } + + public func pendingTransactions(onSubscribe: @escaping (Result) -> Void, onData: @escaping (String) -> Void) { + Task { + do { + let result = try await pendingTransactions(onData: onData) + onSubscribe(.success(result)) + } catch { + failureHandler(error, completionHandler: onSubscribe) + } + } + } + + public func newBlockHeaders(onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumHeader) -> Void) { + Task { + do { + let result = try await newBlockHeaders(onData: onData) + onSubscribe(.success(result)) + } catch { + failureHandler(error, completionHandler: onSubscribe) + } + } + } + + public func syncing(onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumSyncStatus) -> Void) { + Task { + do { + let result = try await syncing(onData: onData) + onSubscribe(.success(result)) + } catch { + failureHandler(error, completionHandler: onSubscribe) + } } } } -} #endif diff --git a/web3swift/src/Client/Models/EthereumAddress.swift b/web3swift/src/Client/Models/EthereumAddress.swift index a33b9b1c..da57cfe5 100644 --- a/web3swift/src/Client/Models/EthereumAddress.swift +++ b/web3swift/src/Client/Models/EthereumAddress.swift @@ -28,7 +28,7 @@ public struct EthereumAddress: Codable, Hashable { } public static func == (lhs: EthereumAddress, rhs: EthereumAddress) -> Bool { - return lhs.value == rhs.value + lhs.value == rhs.value } } @@ -50,7 +50,6 @@ public extension EthereumAddress { } extension EthereumAddress: ExpressibleByStringLiteral { - public init(stringLiteral value: String) { self.init(value) } diff --git a/web3swift/src/Client/Models/EthereumBlock.swift b/web3swift/src/Client/Models/EthereumBlock.swift index efd5074d..53e29023 100644 --- a/web3swift/src/Client/Models/EthereumBlock.swift +++ b/web3swift/src/Client/Models/EthereumBlock.swift @@ -19,14 +19,14 @@ public enum EthereumBlock: Hashable { return "earliest" case .Pending: return "pending" - case .Number(let int): + case let .Number(int): return int.web3.hexString } } public var intValue: Int? { switch self { - case .Number(let int): + case let .Number(int): return int default: return nil @@ -65,7 +65,7 @@ extension EthereumBlock: Codable { extension EthereumBlock: Comparable { static public func == (lhs: EthereumBlock, rhs: EthereumBlock) -> Bool { - return lhs.stringValue == rhs.stringValue + lhs.stringValue == rhs.stringValue } static public func < (lhs: EthereumBlock, rhs: EthereumBlock) -> Bool { @@ -76,7 +76,7 @@ extension EthereumBlock: Comparable { return rhs != .Pending ? true : false case .Pending: return true - case .Number(let lhsInt): + case let .Number(lhsInt): switch rhs { case .Earliest: return false @@ -84,10 +84,9 @@ extension EthereumBlock: Comparable { return true case .Pending: return true - case .Number(let rhsInt): + case let .Number(rhsInt): return lhsInt < rhsInt } } - } } diff --git a/web3swift/src/Client/Models/EthereumBlockInfo.swift b/web3swift/src/Client/Models/EthereumBlockInfo.swift index 6a2ff034..bf6d0a70 100644 --- a/web3swift/src/Client/Models/EthereumBlockInfo.swift +++ b/web3swift/src/Client/Models/EthereumBlockInfo.swift @@ -17,6 +17,7 @@ extension EthereumBlockInfo: Codable { case timestamp case transactions } + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) @@ -25,8 +26,8 @@ extension EthereumBlockInfo: Codable { } guard let timestampRaw = try? container.decode(String.self, forKey: .timestamp), - let timestamp = TimeInterval(timestampRaw) else { - throw JSONRPCError.decodingError + let timestamp = TimeInterval(timestampRaw) else { + throw JSONRPCError.decodingError } guard let transactions = try? container.decode([String].self, forKey: .transactions) else { diff --git a/web3swift/src/Client/Models/EthereumLog.swift b/web3swift/src/Client/Models/EthereumLog.swift index 7f9e85af..03f3cbe5 100644 --- a/web3swift/src/Client/Models/EthereumLog.swift +++ b/web3swift/src/Client/Models/EthereumLog.swift @@ -20,15 +20,15 @@ public struct EthereumLog: Equatable { extension EthereumLog: Codable { enum CodingKeys: String, CodingKey { - case removed // Bool - case logIndex // Quantity or null - case transactionIndex // Quantity or null - case transactionHash // Data or null - case blockHash // Data or null - case blockNumber // Data or null - case address // Data - case data // Data - case topics // Array of Data + case removed // Bool + case logIndex // Quantity or null + case transactionIndex // Quantity or null + case transactionHash // Data or null + case blockHash // Data or null + case blockNumber // Data or null + case address // Data + case data // Data + case topics // Array of Data } public init(from decoder: Decoder) throws { @@ -76,16 +76,14 @@ extension EthereumLog: Codable { try container.encode(address, forKey: .address) try container.encode(data, forKey: .data) try container.encode(topics, forKey: .topics) - } - } extension EthereumLog: Comparable { public static func < (lhs: EthereumLog, rhs: EthereumLog) -> Bool { if lhs.blockNumber == rhs.blockNumber, - let lhsIndex = lhs.logIndex, - let rhsIndex = rhs.logIndex { + let lhsIndex = lhs.logIndex, + let rhsIndex = rhs.logIndex { return lhsIndex < rhsIndex } diff --git a/web3swift/src/Client/Models/EthereumNetwork.swift b/web3swift/src/Client/Models/EthereumNetwork.swift index 2d78ccbb..e85779db 100644 --- a/web3swift/src/Client/Models/EthereumNetwork.swift +++ b/web3swift/src/Client/Models/EthereumNetwork.swift @@ -36,7 +36,7 @@ public enum EthereumNetwork: Equatable, Decodable { return "42" case .sepolia: return "11155111" - case .custom(let str): + case let .custom(str): return str } } @@ -51,12 +51,12 @@ public enum EthereumNetwork: Equatable, Decodable { return 42 case .sepolia: return 11155111 - case .custom(let str): + case let .custom(str): return Int(str) ?? 0 } } } -public func ==(lhs: EthereumNetwork, rhs: EthereumNetwork) -> Bool { - return lhs.stringValue == rhs.stringValue +public func == (lhs: EthereumNetwork, rhs: EthereumNetwork) -> Bool { + lhs.stringValue == rhs.stringValue } diff --git a/web3swift/src/Client/Models/EthereumSubscription.swift b/web3swift/src/Client/Models/EthereumSubscription.swift index 0c202930..64f3dc23 100644 --- a/web3swift/src/Client/Models/EthereumSubscription.swift +++ b/web3swift/src/Client/Models/EthereumSubscription.swift @@ -27,7 +27,7 @@ public enum EthereumSubscriptionType: Equatable, Hashable { } var params: String? { - return nil + nil } } diff --git a/web3swift/src/Client/Models/EthereumSyncStatus.swift b/web3swift/src/Client/Models/EthereumSyncStatus.swift index 165c1be7..6368c855 100644 --- a/web3swift/src/Client/Models/EthereumSyncStatus.swift +++ b/web3swift/src/Client/Models/EthereumSyncStatus.swift @@ -35,9 +35,9 @@ public enum ResultUnion: Codable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { - case .bool(let x): + case let .bool(x): try container.encode(x) - case .resultClass(let x): + case let .resultClass(x): try container.encode(x) } } diff --git a/web3swift/src/Client/Models/EthereumTransaction.swift b/web3swift/src/Client/Models/EthereumTransaction.swift index 8226ae80..55d8165d 100644 --- a/web3swift/src/Client/Models/EthereumTransaction.swift +++ b/web3swift/src/Client/Models/EthereumTransaction.swift @@ -126,11 +126,11 @@ public struct EthereumTransaction: EthereumTransactionProtocol, Equatable, Codab self.data = try? container.decode(Data.self, forKey: .data) let decodeHexUInt = { (key: CodingKeys) -> BigUInt? in - return (try? container.decode(String.self, forKey: key)).flatMap { BigUInt(hex: $0)} + (try? container.decode(String.self, forKey: key)).flatMap { BigUInt(hex: $0) } } let decodeHexInt = { (key: CodingKeys) -> Int? in - return (try? container.decode(String.self, forKey: key)).flatMap { Int(hex: $0)} + (try? container.decode(String.self, forKey: key)).flatMap { Int(hex: $0) } } self.value = decodeHexUInt(.value) @@ -180,6 +180,6 @@ public struct SignedTransaction { } public var hash: Data? { - return raw?.web3.keccak256 + raw?.web3.keccak256 } } diff --git a/web3swift/src/Client/Models/EthereumTransactionReceipt.swift b/web3swift/src/Client/Models/EthereumTransactionReceipt.swift index ea446be5..05b4a3d5 100644 --- a/web3swift/src/Client/Models/EthereumTransactionReceipt.swift +++ b/web3swift/src/Client/Models/EthereumTransactionReceipt.swift @@ -24,16 +24,16 @@ public struct EthereumTransactionReceipt: Decodable { public var status: EthereumTransactionReceiptStatus enum CodingKeys: String, CodingKey { - case transactionHash // Data - case transactionIndex // Quantity - case blockHash // Data - case blockNumber // Quantity - case cumulativeGasUsed // Quantity - case gasUsed // Quantity - case contractAddress // Data or null - case logs // Array - case logsBloom // Data - case status // Quantity (success 1 or failure 0) + case transactionHash // Data + case transactionIndex // Quantity + case blockHash // Data + case blockNumber // Quantity + case cumulativeGasUsed // Quantity + case gasUsed // Quantity + case contractAddress // Data or null + case logs // Array + case logsBloom // Data + case status // Quantity (success 1 or failure 0) } public init(from decoder: Decoder) throws { @@ -61,5 +61,4 @@ public struct EthereumTransactionReceipt: Decodable { self.logs = try values.decode([EthereumLog].self, forKey: .logs) } - } diff --git a/web3swift/src/Client/NetworkProviders/EventLoopGroupProvider.swift b/web3swift/src/Client/NetworkProviders/EventLoopGroupProvider.swift index 25114091..3c479223 100644 --- a/web3swift/src/Client/NetworkProviders/EventLoopGroupProvider.swift +++ b/web3swift/src/Client/NetworkProviders/EventLoopGroupProvider.swift @@ -5,12 +5,12 @@ #if canImport(NIO) -import Foundation -import NIOCore + import NIOCore + import Foundation -public enum EventLoopGroupProvider { - case shared(EventLoopGroup) - case createNew -} + public enum EventLoopGroupProvider { + case shared(EventLoopGroup) + case createNew + } #endif diff --git a/web3swift/src/Client/NetworkProviders/HttpNetworkProvider.swift b/web3swift/src/Client/NetworkProviders/HttpNetworkProvider.swift index 62b072b4..0683ce3a 100644 --- a/web3swift/src/Client/NetworkProviders/HttpNetworkProvider.swift +++ b/web3swift/src/Client/NetworkProviders/HttpNetworkProvider.swift @@ -6,7 +6,7 @@ import Foundation #if canImport(FoundationNetworking) -import FoundationNetworking + import FoundationNetworking #endif class HttpNetworkProvider: NetworkProviderProtocol { @@ -33,7 +33,7 @@ class HttpNetworkProvider: NetworkProviderProtocol { request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue("application/json", forHTTPHeaderField: "Accept") - let id: Int = 1 + let id = 1 let rpcRequest = JSONRPCRequest(jsonrpc: "2.0", method: method, params: params, id: id) guard let encoded = try? JSONEncoder().encode(rpcRequest) else { throw JSONRPCError.encodingError @@ -46,7 +46,7 @@ class HttpNetworkProvider: NetworkProviderProtocol { if let result = try? JSONDecoder().decode(JSONRPCResult.self, from: data) { return result.result } else if let result = try? JSONDecoder().decode([JSONRPCResult].self, from: data) { - let resultObjects = result.map { return $0.result } + let resultObjects = result.map { $0.result } return resultObjects } else if let errorResult = try? JSONDecoder().decode(JSONRPCErrorResult.self, from: data) { throw JSONRPCError.executionError(errorResult) diff --git a/web3swift/src/Client/NetworkProviders/NetworkProviderProtocol.swift b/web3swift/src/Client/NetworkProviders/NetworkProviderProtocol.swift index 86e9484b..baea710b 100644 --- a/web3swift/src/Client/NetworkProviders/NetworkProviderProtocol.swift +++ b/web3swift/src/Client/NetworkProviders/NetworkProviderProtocol.swift @@ -3,14 +3,13 @@ // Copyright © 2022 Argent Labs Limited. All rights reserved. // - import Foundation #if canImport(NIO) -import NIOWebSocket + import NIOWebSocket #endif #if canImport(FoundationNetworking) -import FoundationNetworking + import FoundationNetworking #endif internal protocol NetworkProviderProtocol { @@ -19,15 +18,15 @@ internal protocol NetworkProviderProtocol { } #if canImport(NIO) -internal protocol WebSocketNetworkProviderProtocol: NetworkProviderProtocol { - var delegate: EthereumWebSocketClientDelegate? { get set } - var onReconnectCallback: (() -> Void)? { get set } - var currentState: WebSocketState { get } - func connect() - func disconnect(code: WebSocketErrorCode) - func refresh() - func reconnect() - func addSubscription(_ subscription: EthereumSubscription, callback: @escaping (Any) -> Void) - func removeSubscription(_ subscription: EthereumSubscription) -} + internal protocol WebSocketNetworkProviderProtocol: NetworkProviderProtocol { + var delegate: EthereumWebSocketClientDelegate? { get set } + var onReconnectCallback: (() -> Void)? { get set } + var currentState: WebSocketState { get } + func connect() + func disconnect(code: WebSocketErrorCode) + func refresh() + func reconnect() + func addSubscription(_ subscription: EthereumSubscription, callback: @escaping (Any) -> Void) + func removeSubscription(_ subscription: EthereumSubscription) + } #endif diff --git a/web3swift/src/Client/NetworkProviders/WebSocketConfiguration.swift b/web3swift/src/Client/NetworkProviders/WebSocketConfiguration.swift index b5f1902d..94aeb42b 100644 --- a/web3swift/src/Client/NetworkProviders/WebSocketConfiguration.swift +++ b/web3swift/src/Client/NetworkProviders/WebSocketConfiguration.swift @@ -5,40 +5,42 @@ #if canImport(NIO) -import Foundation -import NIOSSL + import NIOSSL + import Foundation -public struct WebSocketConfiguration { - /// The TLS configuration for client use. - public var tlsConfiguration: TLSConfiguration? - /// The largest incoming `WebSocketFrame` size in bytes. Default is 16,384 bytes. - public var maxFrameSize: Int - /// Whether or not the websocket should attempt to connect immediately upon instantiation. - public var automaticOpen: Bool - /// The number of milliseconds to delay before attempting to reconnect. - public var reconnectInterval: Int - /// The maximum number of milliseconds to delay a reconnection attempt. - public var maxReconnectInterval: Int - /// The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. - public var reconnectDecay: Double - /// The maximum number of reconnection attempts to make. Unlimited if zero. - public var maxReconnectAttempts: Int + public struct WebSocketConfiguration { + /// The TLS configuration for client use. + public var tlsConfiguration: TLSConfiguration? + /// The largest incoming `WebSocketFrame` size in bytes. Default is 16,384 bytes. + public var maxFrameSize: Int + /// Whether or not the websocket should attempt to connect immediately upon instantiation. + public var automaticOpen: Bool + /// The number of milliseconds to delay before attempting to reconnect. + public var reconnectInterval: Int + /// The maximum number of milliseconds to delay a reconnection attempt. + public var maxReconnectInterval: Int + /// The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. + public var reconnectDecay: Double + /// The maximum number of reconnection attempts to make. Unlimited if zero. + public var maxReconnectAttempts: Int - public init(tlsConfiguration: TLSConfiguration? = nil, - maxFrameSize: Int = 1 << 14, - automaticOpen: Bool = true, - reconnectInterval: Int = 1000, - maxReconnectInterval: Int = 30000, - reconnectDecay: Double = 1.5, - maxReconnectAttempts: Int = 0) { - self.tlsConfiguration = tlsConfiguration - self.maxFrameSize = maxFrameSize - self.automaticOpen = automaticOpen - self.reconnectInterval = reconnectInterval - self.maxReconnectInterval = maxReconnectInterval - self.reconnectDecay = reconnectDecay - self.maxReconnectAttempts = maxReconnectAttempts + public init( + tlsConfiguration: TLSConfiguration? = nil, + maxFrameSize: Int = 1 << 14, + automaticOpen: Bool = true, + reconnectInterval: Int = 1000, + maxReconnectInterval: Int = 30000, + reconnectDecay: Double = 1.5, + maxReconnectAttempts: Int = 0 + ) { + self.tlsConfiguration = tlsConfiguration + self.maxFrameSize = maxFrameSize + self.automaticOpen = automaticOpen + self.reconnectInterval = reconnectInterval + self.maxReconnectInterval = maxReconnectInterval + self.reconnectDecay = reconnectDecay + self.maxReconnectAttempts = maxReconnectAttempts + } } -} #endif diff --git a/web3swift/src/Client/NetworkProviders/WebSocketNetworkProvider.swift b/web3swift/src/Client/NetworkProviders/WebSocketNetworkProvider.swift index aa904c04..f4970a5c 100644 --- a/web3swift/src/Client/NetworkProviders/WebSocketNetworkProvider.swift +++ b/web3swift/src/Client/NetworkProviders/WebSocketNetworkProvider.swift @@ -5,423 +5,440 @@ #if canImport(NIO) -import BigInt -import Foundation -import GenericJSON -import Logging -import NIO -import NIOCore -import NIOSSL -import NIOWebSocket -import WebSocketKit - -#if canImport(FoundationNetworking) -import FoundationNetworking -#endif - -class WebSocketNetworkProvider: WebSocketNetworkProviderProtocol { - private struct WebSocketRequest { - var payload: String - var callback: (Result) -> Void - } + import NIO + import BigInt + import NIOSSL + import Logging + import NIOCore + import Foundation + import GenericJSON + import NIOWebSocket + import WebSocketKit + + #if canImport(FoundationNetworking) + import FoundationNetworking + #endif + + class WebSocketNetworkProvider: WebSocketNetworkProviderProtocol { + private struct WebSocketRequest { + var payload: String + var callback: (Result) -> Void + } - private class SharedResources { - private let semaphore = DispatchSemaphore(value: 1) - // Requests that have not sent yet - private(set) var requestQueue: [Int: WebSocketRequest] = [:] - // Requests that have been sent and waiting for Response - private(set) var responseQueue: [Int: WebSocketRequest] = [:] - // List with current subscriptions - private(set) var subscriptions: [EthereumSubscription: (Any) -> Void] = [:] - - private(set) var reconnectAttempts = 0 - private(set) var forcedClose = false - private(set) var timedOut = false - - private(set) var counter: Int = 0 { - didSet { - if counter == Int.max { - counter = 0 + private class SharedResources { + private let semaphore = DispatchSemaphore(value: 1) + // Requests that have not sent yet + private(set) var requestQueue: [Int: WebSocketRequest] = [:] + // Requests that have been sent and waiting for Response + private(set) var responseQueue: [Int: WebSocketRequest] = [:] + // List with current subscriptions + private(set) var subscriptions: [EthereumSubscription: (Any) -> Void] = [:] + + private(set) var reconnectAttempts = 0 + private(set) var forcedClose = false + private(set) var timedOut = false + + private(set) var counter: Int = 0 { + didSet { + if counter == Int.max { + counter = 0 + } } } - } - func addRequest(_ key: Int, request: WebSocketRequest) { - exclusiveAccess(requestQueue[key] = request) - } + // swiftformat:disable redundantClosure - func removeRequest(_ key: Int) { - exclusiveAccess({requestQueue.removeValue(forKey: key)}()) - } + func addRequest(_ key: Int, request: WebSocketRequest) { + exclusiveAccess(requestQueue[key] = request) + } - func addResponse(_ key: Int, request: WebSocketRequest) { - exclusiveAccess(responseQueue[key] = request) - } + func removeRequest(_ key: Int) { + exclusiveAccess({ requestQueue.removeValue(forKey: key) }()) + } - func removeResponse(_ key: Int) { - exclusiveAccess({responseQueue.removeValue(forKey: key)}()) - } + func addResponse(_ key: Int, request: WebSocketRequest) { + exclusiveAccess(responseQueue[key] = request) + } - func addSubscription(_ subscription: EthereumSubscription, callback: @escaping (Any) -> Void) { - exclusiveAccess(subscriptions[subscription] = callback) - } + func removeResponse(_ key: Int) { + exclusiveAccess({ responseQueue.removeValue(forKey: key) }()) + } - func removeSubscription(_ subscription: EthereumSubscription) { - exclusiveAccess({subscriptions.removeValue(forKey: subscription)}()) - } + func addSubscription(_ subscription: EthereumSubscription, callback: @escaping (Any) -> Void) { + exclusiveAccess(subscriptions[subscription] = callback) + } - func incrementCounter() { - exclusiveAccess(counter += 1) - } + func removeSubscription(_ subscription: EthereumSubscription) { + exclusiveAccess({ subscriptions.removeValue(forKey: subscription) }()) + } - func incrementReconnectAttempts() { - exclusiveAccess(reconnectAttempts += 1) - } + func incrementCounter() { + exclusiveAccess(counter += 1) + } - func resetReconnectAttempts() { - exclusiveAccess(reconnectAttempts = 0) - } + func incrementReconnectAttempts() { + exclusiveAccess(reconnectAttempts += 1) + } - func toggleForcedClosed(closed: Bool) { - exclusiveAccess(forcedClose = closed) - } + func resetReconnectAttempts() { + exclusiveAccess(reconnectAttempts = 0) + } - func toggleTimedOut(timedOut: Bool) { - exclusiveAccess(self.timedOut = timedOut) + func toggleForcedClosed(closed: Bool) { + exclusiveAccess(forcedClose = closed) + } + + func toggleTimedOut(timedOut: Bool) { + exclusiveAccess(self.timedOut = timedOut) + } + + func cleanSubscriptions() { + exclusiveAccess(subscriptions.removeAll()) + } + + // swiftformat:enable redundantClosure + + private func exclusiveAccess(_ access: @autoclosure () -> Void) { + semaphore.wait() + access() + semaphore.signal() + } } - func cleanSubscriptions() { - exclusiveAccess(subscriptions.removeAll()) + weak var delegate: EthereumWebSocketClientDelegate? + + var session: URLSession + var onReconnectCallback: (() -> Void)? + let url: String + let eventLoopGroup: EventLoopGroup + + private(set) var currentState: WebSocketState = .closed + + private let eventLoopGroupProvider: EventLoopGroupProvider + private let logger: Logger + private let configuration: WebSocketConfiguration + private let resources = SharedResources() + + private var retreivedNetwork: EthereumNetwork? + private var webSocket: WebSocket? + + // won't ship with production code thanks to #if DEBUG + // WebSocket is need it for testing purposes + #if DEBUG + func exposeWebSocket() -> WebSocket? { + webSocket + } + #endif + + required init( + url: String, + eventLoopGroupProvider: EventLoopGroupProvider = .createNew, + configuration: WebSocketConfiguration = .init(), + session: URLSession, + logger: Logger? = nil + ) { + self.url = url + self.eventLoopGroupProvider = eventLoopGroupProvider + switch eventLoopGroupProvider { + case let .shared(group): + self.eventLoopGroup = group + case .createNew: + self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + } + self.logger = logger ?? Logger(label: "web3.swift.eth-websocket-client") + self.configuration = configuration + self.session = session + // Whether or not to create a websocket upon instantiation + if configuration.automaticOpen { + connect(reconnectAttempt: false) + } } - private func exclusiveAccess(_ access: @autoclosure () -> Void) { - semaphore.wait() - access() - semaphore.signal() + deinit { + self.logger.trace("Shutting down WebSocket") + disconnect() + + switch self.eventLoopGroupProvider { + case .shared: + self.logger.trace("Running on shared EventLoopGroup. Not shutting down EventLoopGroup.") + case .createNew: + self.logger.trace("Shutting down EventLoopGroup") + do { + try self.eventLoopGroup.syncShutdownGracefully() + } catch { + self.logger.warning("Shutting down EventLoopGroup failed: \(error)") + } + } } - } - weak var delegate: EthereumWebSocketClientDelegate? + func send(method: String, params: P, receive: U.Type) async throws -> Any where P: Encodable, U: Decodable { + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + resources.incrementCounter() + let id = resources.counter - var session: URLSession - var onReconnectCallback: (() -> Void)? - let url: String - let eventLoopGroup: EventLoopGroup + let requestString: String - private(set) var currentState: WebSocketState = .closed + do { + requestString = try encodeRequest(method: method, params: params, id: id) + } catch { + continuation.resume(throwing: EthereumClientError.encodeIssue) + return + } - private let eventLoopGroupProvider: EventLoopGroupProvider - private let logger: Logger - private let configuration: WebSocketConfiguration - private let resources = SharedResources() + let wsRequest = WebSocketRequest(payload: requestString, callback: decoding(receive.self) { result in + continuation.resume(with: result) + }) - private var retreivedNetwork: EthereumNetwork? - private var webSocket: WebSocket? + // if socket is not connected yet or reconnecting + // add request to the queue + if currentState == .connecting { + resources.addRequest(id, request: wsRequest) + return + } - // won't ship with production code thanks to #if DEBUG - // WebSocket is need it for testing purposes -#if DEBUG - func exposeWebSocket() -> WebSocket? { - return webSocket - } -#endif + // if socket is closed remove pending request + // and return failure + if currentState != .open { + resources.removeRequest(id) + continuation.resume(throwing: EthereumClientError.connectionNotOpen) + return + } + + resources.addResponse(id, request: wsRequest) + resources.removeRequest(id) - required init(url: String, - eventLoopGroupProvider: EventLoopGroupProvider = .createNew, - configuration: WebSocketConfiguration = .init(), - session: URLSession, - logger: Logger? = nil) { - self.url = url - self.eventLoopGroupProvider = eventLoopGroupProvider - switch eventLoopGroupProvider { - case .shared(let group): - self.eventLoopGroup = group - case .createNew: - self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + let sendPromise = eventLoopGroup.next().makePromise(of: Void.self) + sendPromise.futureResult.whenFailure({ error in + continuation.resume(throwing: EthereumClientError.webSocketError(EquatableError(base: error))) + self.resources.removeResponse(id) + }) + webSocket?.send(requestString, promise: sendPromise) + } } - self.logger = logger ?? Logger(label: "web3.swift.eth-websocket-client") - self.configuration = configuration - self.session = session - // Whether or not to create a websocket upon instantiation - if configuration.automaticOpen { + + func connect() { connect(reconnectAttempt: false) } - } - deinit { - self.logger.trace("Shutting down WebSocket") - disconnect() - - switch self.eventLoopGroupProvider { - case .shared: - self.logger.trace("Running on shared EventLoopGroup. Not shutting down EventLoopGroup.") - case .createNew: - self.logger.trace("Shutting down EventLoopGroup") + func disconnect(code: WebSocketErrorCode = .goingAway) { do { - try self.eventLoopGroup.syncShutdownGracefully() + resources.toggleForcedClosed(closed: true) + try webSocket?.close(code: code).wait() } catch { - self.logger.warning("Shutting down EventLoopGroup failed: \(error)") + logger.warning("Clossing WebSocket failed: \(error)") } } - } - - func send(method: String, params: P, receive: U.Type) async throws -> Any where P: Encodable, U: Decodable { - return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - resources.incrementCounter() - let id = resources.counter - - let requestString: String + /// Additional public API method to refresh the connection if still open (close, re-open). + /// For example, if the app suspects bad data / missed heart beats, it can try to refresh. + func refresh() { do { - requestString = try encodeRequest(method: method, params: params, id: id) + try webSocket?.close(code: .goingAway).wait() } catch { - continuation.resume(throwing: EthereumClientError.encodeIssue) - return + logger.warning("Failed to Close WebSocket: \(error)") } + } - let wsRequest = WebSocketRequest(payload: requestString, callback: decoding(receive.self) { result in - continuation.resume(with: result) - }) - - // if socket is not connected yet or reconnecting - // add request to the queue - if currentState == .connecting { - resources.addRequest(id, request: wsRequest) - return + func reconnect() { + for response in resources.responseQueue { + response.value.callback(.failure(.pendingRequestsOnReconnecting)) + resources.removeResponse(response.key) } - // if socket is closed remove pending request - // and return failure - if currentState != .open { - resources.removeRequest(id) - continuation.resume(throwing: EthereumClientError.connectionNotOpen) - return + var delay = configuration.reconnectInterval * Int(pow(configuration.reconnectDecay, Double(resources.reconnectAttempts))) + if delay > configuration.maxReconnectInterval { + delay = configuration.maxReconnectInterval } - resources.addResponse(id, request: wsRequest) - resources.removeRequest(id) + logger.trace("WebSocket reconnecting... Delay: \(delay) ms") - let sendPromise = eventLoopGroup.next().makePromise(of: Void.self) - sendPromise.futureResult.whenFailure({ error in - continuation.resume(throwing: EthereumClientError.webSocketError(EquatableError(base: error))) - self.resources.removeResponse(id) - }) - webSocket?.send(requestString, promise: sendPromise) - } - } - - func connect() { - connect(reconnectAttempt: false) - } - - func disconnect(code: WebSocketErrorCode = .goingAway) { - do { - resources.toggleForcedClosed(closed: true) - try webSocket?.close(code: code).wait() - } catch { - logger.warning("Clossing WebSocket failed: \(error)") - } - } - - /// Additional public API method to refresh the connection if still open (close, re-open). - /// For example, if the app suspects bad data / missed heart beats, it can try to refresh. - func refresh() { - do { - try webSocket?.close(code: .goingAway).wait() - } catch { - logger.warning("Failed to Close WebSocket: \(error)") - } - } - - func reconnect() { - for response in resources.responseQueue { - response.value.callback(.failure(.pendingRequestsOnReconnecting)) - resources.removeResponse(response.key) + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(delay)) { [weak self] in + self?.resources.incrementReconnectAttempts() + self?.connect(reconnectAttempt: true) + } } - var delay = configuration.reconnectInterval * Int(pow(configuration.reconnectDecay, Double(resources.reconnectAttempts))) - if delay > configuration.maxReconnectInterval { - delay = configuration.maxReconnectInterval + func addSubscription(_ subscription: EthereumSubscription, callback: @escaping (Any) -> Void) { + resources.addSubscription(subscription, callback: callback) } - logger.trace("WebSocket reconnecting... Delay: \(delay) ms") - - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(delay)) { [weak self] in - self?.resources.incrementReconnectAttempts() - self?.connect(reconnectAttempt: true) + func removeSubscription(_ subscription: EthereumSubscription) { + resources.removeSubscription(subscription) } - } - - func addSubscription(_ subscription: EthereumSubscription, callback: @escaping (Any) -> Void) { - resources.addSubscription(subscription, callback: callback) - } - - func removeSubscription(_ subscription: EthereumSubscription) { - resources.removeSubscription(subscription) - } - private func connect(reconnectAttempt: Bool) { - if let ws = webSocket, !ws.isClosed { - return - } + private func connect(reconnectAttempt: Bool) { + if let ws = webSocket, !ws.isClosed { + return + } - if reconnectAttempt, configuration.maxReconnectAttempts > 0, resources.reconnectAttempts > configuration.maxReconnectAttempts { - logger.trace("WebSocket reached maxReconnectAttempts. Stop trying") + if reconnectAttempt, configuration.maxReconnectAttempts > 0, resources.reconnectAttempts > configuration.maxReconnectAttempts { + logger.trace("WebSocket reached maxReconnectAttempts. Stop trying") - for request in resources.requestQueue { - request.value.callback(.failure(.maxAttemptsReachedOnReconnecting)) - resources.removeRequest(request.key) + for request in resources.requestQueue { + request.value.callback(.failure(.maxAttemptsReachedOnReconnecting)) + resources.removeRequest(request.key) + } + resources.resetReconnectAttempts() + return } - resources.resetReconnectAttempts() - return - } - logger.trace("Requesting WebSocket connection") + logger.trace("Requesting WebSocket connection") - do { - currentState = .connecting + do { + currentState = .connecting + + _ = try WebSocket.connect( + to: url, + configuration: WebSocketClient.Configuration( + tlsConfiguration: configuration.tlsConfiguration, + maxFrameSize: configuration.maxFrameSize + ), + on: eventLoopGroup + ) { [weak self] ws in + guard let self = self else { + return + } - _ = try WebSocket.connect(to: url, - configuration: WebSocketClient.Configuration(tlsConfiguration: configuration.tlsConfiguration, - maxFrameSize: configuration.maxFrameSize), - on: eventLoopGroup) { [weak self] ws in - guard let self = self else { return } - - self.logger.trace("WebSocket connected") + self.logger.trace("WebSocket connected") - if reconnectAttempt { - self.delegate?.onWebSocketReconnect() - self.onReconnectCallback?() - } + if reconnectAttempt { + self.delegate?.onWebSocketReconnect() + self.onReconnectCallback?() + } - self.webSocket = ws - self.currentState = .open - self.resources.resetReconnectAttempts() + self.webSocket = ws + self.currentState = .open + self.resources.resetReconnectAttempts() - // Send pending requests and delete - for request in self.resources.requestQueue { - ws.send(request.value.payload) - self.resources.removeRequest(request.key) - } + // Send pending requests and delete + for request in self.resources.requestQueue { + ws.send(request.value.payload) + self.resources.removeRequest(request.key) + } - ws.onText { [weak self] _, string in - guard let self = self else { return } - - if let data = string.data(using: .utf8), - let json = try? JSONDecoder().decode(JSON.self, from: data), - let subscriptionId = json["params"]?.objectValue?["subscription"]?.stringValue, - let subscription = self.resources.subscriptions.first(where: { $0.key.id == subscriptionId }) { - switch subscription.key.type { - case .newBlockHeaders: - if let data = string.data(using: .utf8), let response = try? JSONDecoder().decode(JSONRPCSubscriptionResponse.self, from: data) { - self.delegate?.onNewBlockHeader(subscription: subscription.key, header: response.params.result) - subscription.value(response.params.result) - } - case .pendingTransactions: - if let data = string.data(using: .utf8), let response = try? JSONDecoder().decode(JSONRPCSubscriptionResponse.self, from: data) { - self.delegate?.onNewPendingTransaction(subscription: subscription.key, txHash: response.params.result) - subscription.value(response.params.result) - } - case .syncing: - if let data = string.data(using: .utf8), let response = try? JSONDecoder().decode(JSONRPCSubscriptionResponse.self, from: data) { - self.delegate?.onSyncing(subscription: subscription.key, sync: response.params.result) - subscription.value(response.params.result) + ws.onText { [weak self] _, string in + guard let self = self else { + return + } + + if let data = string.data(using: .utf8), + let json = try? JSONDecoder().decode(JSON.self, from: data), + let subscriptionId = json["params"]?.objectValue?["subscription"]?.stringValue, + let subscription = self.resources.subscriptions.first(where: { $0.key.id == subscriptionId }) { + switch subscription.key.type { + case .newBlockHeaders: + if let data = string.data(using: .utf8), let response = try? JSONDecoder().decode(JSONRPCSubscriptionResponse.self, from: data) { + self.delegate?.onNewBlockHeader(subscription: subscription.key, header: response.params.result) + subscription.value(response.params.result) + } + case .pendingTransactions: + if let data = string.data(using: .utf8), let response = try? JSONDecoder().decode(JSONRPCSubscriptionResponse.self, from: data) { + self.delegate?.onNewPendingTransaction(subscription: subscription.key, txHash: response.params.result) + subscription.value(response.params.result) + } + case .syncing: + if let data = string.data(using: .utf8), let response = try? JSONDecoder().decode(JSONRPCSubscriptionResponse.self, from: data) { + self.delegate?.onSyncing(subscription: subscription.key, sync: response.params.result) + subscription.value(response.params.result) + } } } - } - if let data = string.data(using: .utf8), - let json = try? JSONDecoder().decode(JSON.self, from: data), - let responseId = json["id"]?.doubleValue { - guard let response = self.resources.responseQueue.first(where: { $0.key == Int(responseId) }) else { return } - response.value.callback(.success(data)) - self.resources.removeResponse(response.key) + if let data = string.data(using: .utf8), + let json = try? JSONDecoder().decode(JSON.self, from: data), + let responseId = json["id"]?.doubleValue { + guard let response = self.resources.responseQueue.first(where: { $0.key == Int(responseId) }) else { + return + } + response.value.callback(.success(data)) + self.resources.removeResponse(response.key) + } } - } - ws.onClose.whenComplete { [weak self] value in - guard let self = self else { return } + ws.onClose.whenComplete { [weak self] value in + guard let self = self else { + return + } - if let code = ws.closeCode { - self.logger.trace("WebSocket closed. Code: \(code)") - } else { - self.logger.trace("WebSocket closed") - } + if let code = ws.closeCode { + self.logger.trace("WebSocket closed. Code: \(code)") + } else { + self.logger.trace("WebSocket closed") + } - for request in self.resources.requestQueue { - request.value.callback(.failure(.connectionNotOpen)) - self.resources.removeRequest(request.key) - } + for request in self.resources.requestQueue { + request.value.callback(.failure(.connectionNotOpen)) + self.resources.removeRequest(request.key) + } - for response in self.resources.responseQueue { - response.value.callback(.failure(.invalidConnection)) - self.resources.removeResponse(response.key) - } + for response in self.resources.responseQueue { + response.value.callback(.failure(.invalidConnection)) + self.resources.removeResponse(response.key) + } - self.resources.cleanSubscriptions() + self.resources.cleanSubscriptions() - if self.resources.forcedClose { - self.currentState = .closed - } else { - self.currentState = .connecting + if self.resources.forcedClose { + self.currentState = .closed + } else { + self.currentState = .connecting - self.reconnect() + self.reconnect() + } } - } - }.wait() - } catch { - currentState = .closed - logger.error("WebSocket connection failed: \(error)") + }.wait() + } catch { + currentState = .closed + logger.error("WebSocket connection failed: \(error)") - for request in resources.requestQueue { - request.value.callback(.failure(.connectionNotOpen)) - resources.removeRequest(request.key) - } + for request in resources.requestQueue { + request.value.callback(.failure(.connectionNotOpen)) + resources.removeRequest(request.key) + } - for response in resources.responseQueue { - response.value.callback(.failure(.connectionNotOpen)) - resources.removeResponse(response.key) - } + for response in resources.responseQueue { + response.value.callback(.failure(.connectionNotOpen)) + resources.removeResponse(response.key) + } - resources.cleanSubscriptions() + resources.cleanSubscriptions() - if case ChannelError.connectTimeout = error { - reconnect() + if case ChannelError.connectTimeout = error { + reconnect() + } } } - } - private func encodeRequest(method: String, params: P, id: Int) throws -> String { - let rpcRequest = JSONRPCRequest(jsonrpc: "2.0", method: method, params: params, id: id) - logger.trace("\(rpcRequest)") - let data = try JSONEncoder().encode(rpcRequest) + private func encodeRequest(method: String, params: P, id: Int) throws -> String { + let rpcRequest = JSONRPCRequest(jsonrpc: "2.0", method: method, params: params, id: id) + logger.trace("\(rpcRequest)") + let data = try JSONEncoder().encode(rpcRequest) - guard let dataString = String(data: data, encoding: .utf8) else { - throw JSONRPCError.encodingError - } + guard let dataString = String(data: data, encoding: .utf8) else { + throw JSONRPCError.encodingError + } - return dataString - } + return dataString + } - private func decoding(_ type: T.Type, then: @escaping (Result) -> Void) -> (Result) -> Void { - return { dataResult in - let decodedResult: Result = dataResult.tryMap { data in - if let result = try? JSONDecoder().decode(JSONRPCResult.self, from: data) { - return result.result - } else if let result = try? JSONDecoder().decode([JSONRPCResult].self, from: data) { - let resultObjects = result.map { return $0.result } - return resultObjects - } else if let errorResult = try? JSONDecoder().decode(JSONRPCErrorResult.self, from: data) { - throw JSONRPCError.executionError(errorResult) - } else { - throw JSONRPCError.noResult + private func decoding(_ type: T.Type, then: @escaping (Result) -> Void) -> (Result) -> Void { + { dataResult in + let decodedResult: Result = dataResult.tryMap { data in + if let result = try? JSONDecoder().decode(JSONRPCResult.self, from: data) { + return result.result + } else if let result = try? JSONDecoder().decode([JSONRPCResult].self, from: data) { + let resultObjects = result.map { $0.result } + return resultObjects + } else if let errorResult = try? JSONDecoder().decode(JSONRPCErrorResult.self, from: data) { + throw JSONRPCError.executionError(errorResult) + } else { + throw JSONRPCError.noResult + } } - + then(decodedResult) } - then(decodedResult) } } -} #endif diff --git a/web3swift/src/Client/RecursiveLogCollector.swift b/web3swift/src/Client/RecursiveLogCollector.swift index 23f52679..dc12a2ee 100644 --- a/web3swift/src/Client/RecursiveLogCollector.swift +++ b/web3swift/src/Client/RecursiveLogCollector.swift @@ -12,9 +12,9 @@ public enum Topics: Encodable { public func encode(to encoder: Encoder) throws { var container = encoder.unkeyedContainer() switch self { - case .plain(let values): + case let .plain(values): try container.encode(contentsOf: values) - case .composed(let values): + case let .composed(values): try container.encode(contentsOf: values) } } @@ -33,8 +33,9 @@ struct RecursiveLogCollector { } guard let lhs = try? await getAllLogs(addresses: addresses, topics: topics, from: from, to: middleBlock), - let rhs = try? await getAllLogs(addresses: addresses, topics: topics, from: middleBlock, to: to) - else { throw EthereumClientError.unexpectedReturnValue } + let rhs = try? await getAllLogs(addresses: addresses, topics: topics, from: middleBlock, to: to) else { + throw EthereumClientError.unexpectedReturnValue + } return lhs + rhs } } @@ -42,11 +43,10 @@ struct RecursiveLogCollector { } private func getLogs(addresses: [EthereumAddress]?, topics: Topics? = nil, from: EthereumBlock, to: EthereumBlock) async throws -> [EthereumLog] { - return try await ethClient.getLogs(addresses: addresses, topics: topics, fromBlock: from, toBlock: to) + try await ethClient.getLogs(addresses: addresses, topics: topics, fromBlock: from, toBlock: to) } private func getMiddleBlock(from: EthereumBlock, to: EthereumBlock) async -> EthereumBlock? { - func toBlockNumber() async -> Int? { if let toBlockNumber = to.intValue { return toBlockNumber @@ -57,7 +57,9 @@ struct RecursiveLogCollector { } } - guard let fromBlockNumber = from.intValue, let toBlockNumber = await toBlockNumber() else { return nil } + guard let fromBlockNumber = from.intValue, let toBlockNumber = await toBlockNumber() else { + return nil + } return EthereumBlock(rawValue: fromBlockNumber + (toBlockNumber - fromBlockNumber) / 2) } diff --git a/web3swift/src/Contract/ABIDecoder.swift b/web3swift/src/Contract/ABIDecoder.swift index 100599fc..618cc38f 100644 --- a/web3swift/src/Contract/ABIDecoder.swift +++ b/web3swift/src/Contract/ABIDecoder.swift @@ -14,15 +14,17 @@ public class ABIDecoder { var offset = 0 let expectingArray = asArray || types.count > 1 - if data == "0x" && expectingArray { + if data == "0x", expectingArray { return [] } for type in types { - if data == "0x" && type.isArray { + if data == "0x", type.isArray { result.append([]) } else { - guard let bytes = data.web3.bytesFromHex else { throw ABIError.invalidValue } + guard let bytes = data.web3.bytesFromHex else { + throw ABIError.invalidValue + } let decoded = try decode(bytes, forType: type, offset: offset) result.append(decoded) } @@ -34,19 +36,19 @@ public class ABIDecoder { static func decode(_ data: [UInt8], forType type: ABIRawType, offset: Int = 0) throws -> ABIEntry { switch type { case .FixedBool: - guard data.count > 0 else { + guard !data.isEmpty else { throw ABIError.invalidValue } return try decode(data, forType: ABIRawType.FixedUInt(type.size), offset: offset) case .FixedAddress: - guard data.count > 0 else { + guard !data.isEmpty else { throw ABIError.invalidValue } return try decode(data, forType: ABIRawType.FixedUInt(type.size), offset: offset) case .DynamicString: return try decode(data, forType: ABIRawType.DynamicBytes, offset: offset) case .DynamicBytes: - guard data.count > 0 else { + guard !data.isEmpty else { return [""] } guard let offsetHex = (try decode(data, forType: ABIRawType.FixedUInt(256), offset: offset)).first, let newOffset = Int(hex: offsetHex) else { @@ -61,38 +63,48 @@ public class ABIDecoder { } let lowerRange = newOffset + 32 let upperRange = newOffset + 32 + size - 1 - guard lowerRange <= upperRange else { throw ABIError.invalidValue } - guard data.count > upperRange else { throw ABIError.invalidValue } - let hex = String(hexFromBytes: Array(data[lowerRange...upperRange])) + guard lowerRange <= upperRange else { + throw ABIError.invalidValue + } + guard data.count > upperRange else { + throw ABIError.invalidValue + } + let hex = String(hexFromBytes: Array(data[lowerRange ... upperRange])) return [hex] case .FixedInt: - guard data.count > 0 else { + guard !data.isEmpty else { return [""] } let startIndex = offset + 32 - type.size let endIndex = offset + 31 - guard data.count > endIndex else { throw ABIError.invalidValue } - let buf = Array(data[startIndex...endIndex]) + guard data.count > endIndex else { + throw ABIError.invalidValue + } + let buf = Array(data[startIndex ... endIndex]) return [String(hexFromBytes: buf)] case .FixedUInt: - guard data.count > 0 else { + guard !data.isEmpty else { return [""] } let startIndex = offset + 32 - type.size let endIndex = offset + 31 - guard data.count > endIndex else { throw ABIError.invalidValue } - let hex = String(hexFromBytes: Array(data[startIndex...endIndex])) // Do not use BInt because address is treated as uint160 and BInt is based on 64 bits (160/64 = 2.5) + guard data.count > endIndex else { + throw ABIError.invalidValue + } + let hex = String(hexFromBytes: Array(data[startIndex ... endIndex])) // Do not use BInt because address is treated as uint160 and BInt is based on 64 bits (160/64 = 2.5) return [hex] - case .FixedBytes(let size): - guard data.count > 0 else { + case let .FixedBytes(size): + guard !data.isEmpty else { return [""] } let startIndex = offset let endIndex = offset + size - 1 - guard data.count > endIndex else { throw ABIError.invalidValue } - let hex = String(hexFromBytes: Array(data[startIndex...endIndex])) + guard data.count > endIndex else { + throw ABIError.invalidValue + } + let hex = String(hexFromBytes: Array(data[startIndex ... endIndex])) return [hex] - case .FixedArray(let arrayType, _): + case let .FixedArray(arrayType, _): var result: [String] = [] var size = type.size var newOffset = offset @@ -100,7 +112,7 @@ public class ABIDecoder { try deepDecode(data: data, type: arrayType, result: &result, offset: &newOffset, size: &size) return result // NOTE: Needs analysis to confirm it can handle an inner `DynamicArray` too - case .DynamicArray(let arrayType) where arrayType.isDynamic: + case let .DynamicArray(arrayType) where arrayType.isDynamic: var result: [String] = [] var currentOffset = offset @@ -124,7 +136,7 @@ public class ABIDecoder { } return result - case .DynamicArray(let arrayType): + case let .DynamicArray(arrayType): var result: [String] = [] var newOffset = offset @@ -143,7 +155,7 @@ public class ABIDecoder { try deepDecode(data: data, type: arrayType, result: &result, offset: &newOffset, size: &size) return result - case .Tuple(let types): + case let .Tuple(types): var result: [String] = [] if type.isDynamic { @@ -157,7 +169,8 @@ public class ABIDecoder { result += try decode( tail, forType: type, - offset: newOffset) + offset: newOffset + ) newOffset += type.memory } @@ -168,7 +181,8 @@ public class ABIDecoder { result += try decode( Array(data.dropFirst(newOffset)), forType: type, - offset: 0) + offset: 0 + ) newOffset += type.memory } @@ -178,7 +192,9 @@ public class ABIDecoder { } private static func deepDecode(data: [UInt8], type: ABIRawType, result: inout [String], offset: inout Int, size: inout Int) throws { - if size < 1 { return } + if size < 1 { + return + } let decoded = try decode(data, forType: type, offset: offset) result.append(contentsOf: decoded) diff --git a/web3swift/src/Contract/ABIEncoder.swift b/web3swift/src/Contract/ABIEncoder.swift index 4274865b..78656ce7 100644 --- a/web3swift/src/Contract/ABIEncoder.swift +++ b/web3swift/src/Contract/ABIEncoder.swift @@ -13,27 +13,27 @@ public class ABIEncoder { public var bytes: [UInt8] { switch self { - case .value(bytes: let encoded, _, _): + case let .value(bytes: encoded, _, _): return encoded - case .container(let values, _, _): + case let .container(values, _, _): return values.flatMap(\.bytes) } } var isDynamic: Bool { switch self { - case .value(_, let isDynamic, _): + case let .value(_, isDynamic, _): return isDynamic - case .container(_, let isDynamic, _): + case let .container(_, isDynamic, _): return isDynamic } } var staticLength: Int { switch self { - case .value(_, _, let staticLength): + case let .value(_, _, staticLength): return staticLength - case .container(let values, let isDynamic, _): + case let .container(values, isDynamic, _): if isDynamic { return 32 } else { @@ -45,31 +45,39 @@ public class ABIEncoder { public var hexString: String { String(hexFromBytes: bytes) } } - static func encodeRaw(_ value: Data, - forType type: ABIRawType, - padded: Bool = true, - size: Int = 1) throws -> EncodedValue { - return try encodeRaw(value.web3.hexString, forType: type, padded: padded, size: size) + static func encodeRaw( + _ value: Data, + forType type: ABIRawType, + padded: Bool = true, + size: Int = 1 + ) throws -> EncodedValue { + try encodeRaw(value.web3.hexString, forType: type, padded: padded, size: size) } - static func encodeRaw(_ value: String, - forType type: ABIRawType, - padded: Bool = true, - size: Int = 1) throws -> EncodedValue { + static func encodeRaw( + _ value: String, + forType type: ABIRawType, + padded: Bool = true, + size: Int = 1 + ) throws -> EncodedValue { let encoded: [UInt8] = try encodeRaw(value, forType: type, padded: padded, size: size) - return .value(bytes: encoded, - isDynamic: type.isDynamic, - staticLength: type.isDynamic ? 32 : 32 * size) + return .value( + bytes: encoded, + isDynamic: type.isDynamic, + staticLength: type.isDynamic ? 32 : 32 * size + ) } - private static func encodeRaw(_ value: String, - forType type: ABIRawType, - padded: Bool = true, - size: Int = 1) throws -> [UInt8] { - var encoded: [UInt8] = [UInt8]() + private static func encodeRaw( + _ value: String, + forType type: ABIRawType, + padded: Bool = true, + size: Int = 1 + ) throws -> [UInt8] { + var encoded = [UInt8]() switch type { - case .FixedUInt(let typeSize): + case let .FixedUInt(typeSize): let bytesSize = typeSize / 8 guard let int = value.web3.isNumeric ? BigUInt(value) : BigUInt(hex: value) else { throw ABIError.invalidValue @@ -103,9 +111,11 @@ public class ABIEncoder { encoded = bytes } case .FixedBool: - encoded = try encodeRaw(value == "true" ? "1":"0", forType: ABIRawType.FixedUInt(8), padded: padded) + encoded = try encodeRaw(value == "true" ? "1" : "0", forType: ABIRawType.FixedUInt(8), padded: padded) case .FixedAddress: - guard let bytes = value.web3.bytesFromHex else { throw ABIError.invalidValue } // Must be 20 bytes + guard let bytes = value.web3.bytesFromHex else { + throw ABIError.invalidValue + } // Must be 20 bytes if padded { encoded = [UInt8](repeating: 0x00, count: 32 - bytes.count) + bytes } else { @@ -122,10 +132,12 @@ public class ABIEncoder { } case .DynamicBytes: // Bytes are hex encoded - guard let bytes = value.web3.bytesFromHex else { throw ABIError.invalidValue } + guard let bytes = value.web3.bytesFromHex else { + throw ABIError.invalidValue + } let len = try encodeRaw(String(bytes.count), forType: ABIRawType.FixedUInt(256)).bytes let pack: Int - if bytes.count == 0 { + if bytes.isEmpty { pack = 0 } else { pack = (bytes.count - (bytes.count % 32)) / 32 + 1 @@ -138,30 +150,32 @@ public class ABIEncoder { } case .FixedBytes: // Bytes are hex encoded - guard let bytes = value.web3.bytesFromHex else { throw ABIError.invalidValue } + guard let bytes = value.web3.bytesFromHex else { + throw ABIError.invalidValue + } if padded { encoded = bytes + [UInt8](repeating: 0x00, count: 32 - bytes.count) } else { encoded = bytes } - case .DynamicArray(let type): + case let .DynamicArray(type): let unitSize = type.size * 2 let stringValue = value.web3.noHexPrefix let size = stringValue.count / unitSize let padUnits = type.isPaddedInDynamic var bytes = [UInt8]() - for i in 0.. 0 + case let .Tuple(types): + return !types.filter(\.isDynamic).isEmpty default: return false } @@ -146,9 +154,9 @@ extension ABIRawType: RawRepresentable { return 8 case .FixedAddress: return 160 - case .FixedUInt(let size), .FixedInt(let size): + case let .FixedUInt(size), let .FixedInt(size): return size / 8 - case .FixedBytes(let size), .FixedArray(_, let size): + case let .FixedBytes(size), let .FixedArray(_, size): return size case .DynamicArray: return -1 @@ -159,9 +167,9 @@ extension ABIRawType: RawRepresentable { var memory: Int { switch self { - case .FixedArray(let type, let size): + case let .FixedArray(type, size): return type.memory * size - case .Tuple(let types): + case let .Tuple(types): return types.map(\.memory).reduce(0, +) default: return 32 diff --git a/web3swift/src/Contract/Statically Typed/ABIDecoder+Static.swift b/web3swift/src/Contract/Statically Typed/ABIDecoder+Static.swift index 43c5de49..5543cd68 100644 --- a/web3swift/src/Contract/Statically Typed/ABIDecoder+Static.swift +++ b/web3swift/src/Contract/Statically Typed/ABIDecoder+Static.swift @@ -42,7 +42,7 @@ extension ABIDecoder { var parsed = [T]() var leftElements = entry while leftElements.count >= tupleElements { - let slice = Array(leftElements[0.. String { - return data.web3.stringValue + data.web3.stringValue } public static func decode(_ data: ParsedABIEntry, to: Bool.Type) throws -> Bool { - if data == "0x01"{ + if data == "0x01" { return true } else if data == "0x00" { return false @@ -92,36 +92,56 @@ extension ABIDecoder { } public static func decode(_ data: ParsedABIEntry, to: BigInt.Type) throws -> BigInt { - guard let value = data.web3.hexData.map(BigInt.init(twosComplement:)) else { throw ABIError.invalidValue } + guard let value = data.web3.hexData.map(BigInt.init(twosComplement:)) else { + throw ABIError.invalidValue + } return value } public static func decode(_ data: ParsedABIEntry, to: BigUInt.Type) throws -> BigUInt { - guard let value = BigUInt(hex: data) else { throw ABIError.invalidValue } + guard let value = BigUInt(hex: data) else { + throw ABIError.invalidValue + } return value } public static func decode(_ data: ParsedABIEntry, to: UInt8.Type) throws -> UInt8 { - guard let value = BigUInt(hex: data) else { throw ABIError.invalidValue } - guard value.bitWidth <= 8 else { throw ABIError.invalidValue } + guard let value = BigUInt(hex: data) else { + throw ABIError.invalidValue + } + guard value.bitWidth <= 8 else { + throw ABIError.invalidValue + } return UInt8(value) } public static func decode(_ data: ParsedABIEntry, to: UInt16.Type) throws -> UInt16 { - guard let value = BigUInt(hex: data) else { throw ABIError.invalidValue } - guard value.bitWidth <= 16 else { throw ABIError.invalidValue } + guard let value = BigUInt(hex: data) else { + throw ABIError.invalidValue + } + guard value.bitWidth <= 16 else { + throw ABIError.invalidValue + } return UInt16(value) } public static func decode(_ data: ParsedABIEntry, to: UInt32.Type) throws -> UInt32 { - guard let value = BigUInt(hex: data) else { throw ABIError.invalidValue } - guard value.bitWidth <= 32 else { throw ABIError.invalidValue } + guard let value = BigUInt(hex: data) else { + throw ABIError.invalidValue + } + guard value.bitWidth <= 32 else { + throw ABIError.invalidValue + } return UInt32(value) } public static func decode(_ data: ParsedABIEntry, to: UInt64.Type) throws -> UInt64 { - guard let value = BigUInt(hex: data) else { throw ABIError.invalidValue } - guard value.bitWidth <= 64 else { throw ABIError.invalidValue } + guard let value = BigUInt(hex: data) else { + throw ABIError.invalidValue + } + guard value.bitWidth <= 64 else { + throw ABIError.invalidValue + } return UInt64(value) } @@ -138,8 +158,9 @@ extension ABIDecoder { } public static func decode(_ data: ParsedABIEntry, to: Data.Type) throws -> Data { - guard let data = Data(hex: data) else { throw ABIError.invalidValue } + guard let data = Data(hex: data) else { + throw ABIError.invalidValue + } return data } - } diff --git a/web3swift/src/Contract/Statically Typed/ABIEncoder+Static.swift b/web3swift/src/Contract/Statically Typed/ABIEncoder+Static.swift index 04b14801..8ed34bcf 100644 --- a/web3swift/src/Contract/Statically Typed/ABIEncoder+Static.swift +++ b/web3swift/src/Contract/Statically Typed/ABIEncoder+Static.swift @@ -7,9 +7,11 @@ import BigInt import Foundation extension ABIEncoder { - public static func encode(_ value: ABIType, - staticSize: Int? = nil, - packed: Bool = false) throws -> EncodedValue { + public static func encode( + _ value: ABIType, + staticSize: Int? = nil, + packed: Bool = false + ) throws -> EncodedValue { let type = Swift.type(of: value).rawType switch value { case let value as String: @@ -44,9 +46,11 @@ extension ABIEncoder { } } - public static func encode(_ values: [T], - staticSize: Int? = nil) throws -> EncodedValue { - return try ABIEncoder.encodeArray(elements: values.map { (value: $0, size: nil) }, isDynamic: staticSize == nil, size: values.count) + public static func encode( + _ values: [T], + staticSize: Int? = nil + ) throws -> EncodedValue { + try ABIEncoder.encodeArray(elements: values.map { (value: $0, size: nil) }, isDynamic: staticSize == nil, size: values.count) } private typealias ValueAndSize = (value: ABIType, size: Int?) diff --git a/web3swift/src/Contract/Statically Typed/ABIFunctionEncodable.swift b/web3swift/src/Contract/Statically Typed/ABIFunctionEncodable.swift index 774d0e2a..ab2bf3f3 100644 --- a/web3swift/src/Contract/Statically Typed/ABIFunctionEncodable.swift +++ b/web3swift/src/Contract/Statically Typed/ABIFunctionEncodable.swift @@ -29,7 +29,7 @@ extension ABIFunctionEncodable { let decoded = try ABIDecoder.decodeData(raw, types: expectedTypes) let empty = decoded.flatMap { $0.entry.filter(\.isEmpty) } guard - empty.count == 0 || !filterEmptyEntries, + empty.isEmpty || !filterEmptyEntries, decoded.count == expectedTypes.count else { throw ABIError.invalidSignature } diff --git a/web3swift/src/Contract/Statically Typed/ABIFunctionEncoder.swift b/web3swift/src/Contract/Statically Typed/ABIFunctionEncoder.swift index 5028ecc2..6cb58d50 100644 --- a/web3swift/src/Contract/Statically Typed/ABIFunctionEncoder.swift +++ b/web3swift/src/Contract/Statically Typed/ABIFunctionEncoder.swift @@ -8,7 +8,7 @@ import Foundation public class ABIFunctionEncoder { private let name: String - private (set) var types: [ABIRawType] = [] + private(set) var types: [ABIRawType] = [] public func encode(_ value: ABIType, staticSize: Int? = nil) throws { let rawType = type(of: value).rawType @@ -16,17 +16,17 @@ public class ABIFunctionEncoder { encodedValues.append(encoded) switch (staticSize, rawType) { - case (let size?, .DynamicBytes): + case let (size?, .DynamicBytes): guard size <= 32 else { throw ABIError.invalidType } types.append(.FixedBytes(size)) - case (let size?, .FixedUInt): + case let (size?, .FixedUInt): guard size <= 256 else { throw ABIError.invalidType } types.append(.FixedUInt(size)) - case (let size?, .FixedInt): + case let (size?, .FixedInt): guard size <= 256 else { throw ABIError.invalidType } @@ -57,7 +57,9 @@ public class ABIFunctionEncoder { static func signature(name: String, types: [ABIRawType]) throws -> [UInt8] { let typeNames = types.map { $0.rawValue } let signature = name + "(" + typeNames.joined(separator: ",") + ")" - guard let data = signature.data(using: .utf8) else { throw ABIError.invalidSignature } + guard let data = signature.data(using: .utf8) else { + throw ABIError.invalidSignature + } return data.web3.keccak256.web3.bytes } @@ -70,5 +72,4 @@ public class ABIFunctionEncoder { let signature = try Self.signature(name: name, types: types) return Array(signature.prefix(4)) } - } diff --git a/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift b/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift index f93281b9..14145587 100644 --- a/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift +++ b/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift @@ -17,7 +17,7 @@ extension String: ABIType { public static var rawType: ABIRawType { .DynamicString } public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: String.self) } @@ -27,7 +27,7 @@ extension String: ABIType { extension Bool: ABIType { public static var rawType: ABIRawType { .FixedBool } public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: Bool.self) } @@ -37,16 +37,17 @@ extension Bool: ABIType { extension EthereumAddress: ABIType { public static var rawType: ABIRawType { .FixedAddress } public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: EthereumAddress.self) } - } + } } + extension BigInt: ABIType { public static var rawType: ABIRawType { .FixedInt(256) } public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: BigInt.self) } @@ -56,7 +57,7 @@ extension BigInt: ABIType { extension BigUInt: ABIType { public static var rawType: ABIRawType { .FixedUInt(256) } public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: BigUInt.self) } @@ -65,9 +66,11 @@ extension BigUInt: ABIType { extension UInt8: ABIType { public static var rawType: ABIRawType { - .FixedUInt(8) } + .FixedUInt(8) + } + public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: UInt8.self) } @@ -77,7 +80,7 @@ extension UInt8: ABIType { extension UInt16: ABIType { public static var rawType: ABIRawType { .FixedUInt(16) } public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: UInt16.self) } @@ -87,7 +90,7 @@ extension UInt16: ABIType { extension UInt32: ABIType { public static var rawType: ABIRawType { .FixedUInt(32) } public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: UInt32.self) } @@ -97,7 +100,7 @@ extension UInt32: ABIType { extension UInt64: ABIType { public static var rawType: ABIRawType { .FixedUInt(64) } public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: UInt64.self) } @@ -107,7 +110,7 @@ extension UInt64: ABIType { extension URL: ABIType { public static var rawType: ABIRawType { .DynamicBytes } public static var parser: ParserFunction { - return { data in + { data in let first = data.first ?? "" return try ABIDecoder.decode(first, to: URL.self) } @@ -118,8 +121,9 @@ extension ABITuple { public static var rawType: ABIRawType { .Tuple(Self.types.map { $0.rawType }) } + public static var parser: ParserFunction { - return { data in + { data in let values = data.map { ABIDecoder.DecodedValue(entry: [$0]) } guard let decoded = try? self.init(values: values) else { throw ABIError.invalidValue @@ -407,11 +411,12 @@ public struct ABIArray: ABIType { public init(values: [T]) { self.values = values } + public static var rawType: ABIRawType { .DynamicArray(T.rawType) } public static var parser: ParserFunction { - return T.parser + T.parser } } diff --git a/web3swift/src/Contract/Statically Typed/EthereumClient+Static.swift b/web3swift/src/Contract/Statically Typed/EthereumClient+Static.swift index eccf9e76..ca53ce6c 100644 --- a/web3swift/src/Contract/Statically Typed/EthereumClient+Static.swift +++ b/web3swift/src/Contract/Statically Typed/EthereumClient+Static.swift @@ -14,11 +14,12 @@ public extension ABIFunction { return try await client.eth_sendRawTransaction(tx, withAccount: account) } - func call(withClient client: EthereumClientProtocol, - responseType: T.Type, - block: EthereumBlock = .Latest, - resolution: CallResolution = .noOffchain(failOnExecutionError: true)) async throws -> T { - + func call( + withClient client: EthereumClientProtocol, + responseType: T.Type, + block: EthereumBlock = .Latest, + resolution: CallResolution = .noOffchain(failOnExecutionError: true) + ) async throws -> T { guard let tx = try? transaction() else { throw EthereumClientError.encodeIssue } @@ -56,7 +57,7 @@ public extension ABIFunction { extension CallResolution { var failOnExecutionError: Bool { switch self { - case .noOffchain(let fail): + case let .noOffchain(fail): return fail case .offchainAllowed: return true @@ -77,8 +78,10 @@ public struct EventFilter { public let type: ABIEvent.Type public let allowedSenders: [EthereumAddress] - public init(type: ABIEvent.Type, - allowedSenders: [EthereumAddress]) { + public init( + type: ABIEvent.Type, + allowedSenders: [EthereumAddress] + ) { self.type = type self.allowedSenders = allowedSenders } @@ -90,47 +93,55 @@ public struct Events { } public extension EthereumClientProtocol { - func getEvents(addresses: [EthereumAddress]?, - orTopics: [[String]?]?, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - matching matches: [EventFilter]) async throws -> Events { - + func getEvents( + addresses: [EthereumAddress]?, + orTopics: [[String]?]?, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + matching matches: [EventFilter] + ) async throws -> Events { let logs = try await eth_getLogs(addresses: addresses, orTopics: orTopics, fromBlock: fromBlock, toBlock: toBlock) return handleLogs(logs, matches) } - func getEvents(addresses: [EthereumAddress]?, - orTopics: [[String]?]?, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - eventTypes: [ABIEvent.Type]) async throws -> Events { + func getEvents( + addresses: [EthereumAddress]?, + orTopics: [[String]?]?, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + eventTypes: [ABIEvent.Type] + ) async throws -> Events { let unfiltered = eventTypes.map { EventFilter(type: $0, allowedSenders: []) } let logs = try await eth_getLogs(addresses: addresses, orTopics: orTopics, fromBlock: fromBlock, toBlock: toBlock) return handleLogs(logs, unfiltered) } - func getEvents(addresses: [EthereumAddress]?, - topics: [String?]?, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - eventTypes: [ABIEvent.Type]) async throws -> Events { + func getEvents( + addresses: [EthereumAddress]?, + topics: [String?]?, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + eventTypes: [ABIEvent.Type] + ) async throws -> Events { let unfiltered = eventTypes.map { EventFilter(type: $0, allowedSenders: []) } return try await getEvents(addresses: addresses, topics: topics, fromBlock: fromBlock, toBlock: toBlock, matching: unfiltered) } - func getEvents(addresses: [EthereumAddress]?, - topics: [String?]?, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - matching matches: [EventFilter]) async throws -> Events { - + func getEvents( + addresses: [EthereumAddress]?, + topics: [String?]?, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + matching matches: [EventFilter] + ) async throws -> Events { let logs = try await eth_getLogs(addresses: addresses, topics: topics, fromBlock: fromBlock, toBlock: toBlock) return handleLogs(logs, matches) } - func handleLogs(_ logs: [EthereumLog], - _ matches: [EventFilter]) -> Events { + func handleLogs( + _ logs: [EthereumLog], + _ matches: [EventFilter] + ) -> Events { var events: [ABIEvent] = [] var unprocessed: [EthereumLog] = [] @@ -184,7 +195,7 @@ public extension EthereumClientProtocol { for filter in filters { let allowedSenders = Set(filter.allowedSenders) - if allowedSenders.count > 0 && !allowedSenders.contains(log.address) { + if !allowedSenders.isEmpty, !allowedSenders.contains(log.address) { unprocessed.append(log) } else if let event = parseEvent(log, filter.type) { events.append(event) @@ -200,12 +211,14 @@ public extension EthereumClientProtocol { public extension EthereumClientProtocol { typealias EventsCompletionHandler = (Result) -> Void - func getEvents(addresses: [EthereumAddress]?, - orTopics: [[String]?]?, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - matching matches: [EventFilter], - completionHandler: @escaping EventsCompletionHandler) { + func getEvents( + addresses: [EthereumAddress]?, + orTopics: [[String]?]?, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + matching matches: [EventFilter], + completionHandler: @escaping EventsCompletionHandler + ) { Task { do { let result = try await getEvents(addresses: addresses, orTopics: orTopics, fromBlock: fromBlock, toBlock: toBlock, matching: matches) @@ -216,12 +229,14 @@ public extension EthereumClientProtocol { } } - func getEvents(addresses: [EthereumAddress]?, - orTopics: [[String]?]?, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - eventTypes: [ABIEvent.Type], - completionHandler: @escaping EventsCompletionHandler) { + func getEvents( + addresses: [EthereumAddress]?, + orTopics: [[String]?]?, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + eventTypes: [ABIEvent.Type], + completionHandler: @escaping EventsCompletionHandler + ) { Task { do { let result = try await getEvents(addresses: addresses, orTopics: orTopics, fromBlock: fromBlock, toBlock: toBlock, eventTypes: eventTypes) @@ -232,12 +247,14 @@ public extension EthereumClientProtocol { } } - func getEvents(addresses: [EthereumAddress]?, - topics: [String?]?, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - eventTypes: [ABIEvent.Type], - completionHandler: @escaping EventsCompletionHandler) { + func getEvents( + addresses: [EthereumAddress]?, + topics: [String?]?, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + eventTypes: [ABIEvent.Type], + completionHandler: @escaping EventsCompletionHandler + ) { Task { do { let result = try await getEvents(addresses: addresses, topics: topics, fromBlock: fromBlock, toBlock: toBlock, eventTypes: eventTypes) @@ -248,13 +265,14 @@ public extension EthereumClientProtocol { } } - func getEvents(addresses: [EthereumAddress]?, - topics: [String?]?, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - matching matches: [EventFilter], - completionHandler: @escaping EventsCompletionHandler) { - + func getEvents( + addresses: [EthereumAddress]?, + topics: [String?]?, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + matching matches: [EventFilter], + completionHandler: @escaping EventsCompletionHandler + ) { Task { do { let result = try await getEvents(addresses: addresses, topics: topics, fromBlock: fromBlock, toBlock: toBlock, matching: matches) diff --git a/web3swift/src/ENS/ENSContracts.swift b/web3swift/src/ENS/ENSContracts.swift index 6c3073ff..67fab5ab 100644 --- a/web3swift/src/ENS/ENSContracts.swift +++ b/web3swift/src/ENS/ENSContracts.swift @@ -29,11 +29,11 @@ public enum ENSContracts { var nameHash: Data { let nameHash: String switch self { - case .address(let address): + case let .address(address): nameHash = ENSContracts.nameHash( name: address.value.web3.noHexPrefix + ".addr.reverse" ) - case .name(let ens): + case let .name(ens): nameHash = ENSContracts.nameHash(name: ens) } return nameHash.web3.hexData ?? Data() @@ -41,18 +41,18 @@ public enum ENSContracts { var dnsEncoded: Data { switch self { - case .address(let address): + case let .address(address): return ENSContracts.dnsEncode( name: address.value.web3.noHexPrefix + ".addr.reverse" ) - case .name(let name): + case let .name(name): return ENSContracts.dnsEncode(name: name) } } var name: String? { switch self { - case .name(let ens): + case let .name(ens): return ens case .address: return nil @@ -61,7 +61,7 @@ public enum ENSContracts { var address: EthereumAddress? { switch self { - case .address(let address): + case let .address(address): return address case .name: return nil @@ -123,10 +123,10 @@ public enum ENSContracts { init( contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil, - _node: Data + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + _node: Data ) { self.contract = contract self.from = from @@ -157,7 +157,7 @@ public enum ENSContracts { public enum ENSOffchainResolverFunctions { public static var interfaceId: Data { - return "0x9061b923".web3.hexData! + "0x9061b923".web3.hexData! } public struct resolve: ABIFunction { @@ -314,7 +314,7 @@ public enum ENSContracts { } public struct AddressResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ EthereumAddress.self ] + public static var types: [ABIType.Type] = [EthereumAddress.self] public let value: EthereumAddress public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -323,7 +323,7 @@ public enum ENSContracts { } public struct StringResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ String.self ] + public static var types: [ABIType.Type] = [String.self] public let value: String public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -332,7 +332,7 @@ public enum ENSContracts { } public struct AddressAsDataResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ Data.self ] + public static var types: [ABIType.Type] = [Data.self] public let value: EthereumAddress public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -342,7 +342,7 @@ public enum ENSContracts { } public struct StringAsDataResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ Data.self ] + public static var types: [ABIType.Type] = [Data.self] public let value: String public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -352,7 +352,7 @@ public enum ENSContracts { } static func nameHash(name: String) -> String { - var node = Data.init(count: 32) + var node = Data(count: 32) let labels = name.components(separatedBy: ".") for label in labels.reversed() { node.append(label.web3.keccak256) diff --git a/web3swift/src/ENS/ENSMultiResolver.swift b/web3swift/src/ENS/ENSMultiResolver.swift index e30172ee..4358d322 100644 --- a/web3swift/src/ENS/ENSMultiResolver.swift +++ b/web3swift/src/ENS/ENSMultiResolver.swift @@ -7,12 +7,12 @@ import Foundation extension EthereumNameService { public func resolve(addresses: [EthereumAddress]) async throws -> [AddressResolveOutput] { - return try await MultiResolver(client: client, registryAddress: registryAddress) + try await MultiResolver(client: client, registryAddress: registryAddress) .resolve(addresses: addresses) } public func resolve(names: [String]) async throws -> [NameResolveOutput] { - return try await MultiResolver(client: client, registryAddress: registryAddress) + try await MultiResolver(client: client, registryAddress: registryAddress) .resolve(names: names) } } @@ -64,7 +64,6 @@ extension EthereumNameService { } private class MultiResolver { - private class RegistryOutput { var queries: [ResolverQuery] = [] var intermediaryResponses: [ResolveOutput?] @@ -85,8 +84,10 @@ extension EthereumNameService { let registryAddress: EthereumAddress? let multicall: Multicall - init(client: EthereumClientProtocol, - registryAddress: EthereumAddress? = nil) { + init( + client: EthereumClientProtocol, + registryAddress: EthereumAddress? = nil + ) { self.client = client self.registryAddress = registryAddress self.multicall = Multicall(client: client) @@ -96,9 +97,11 @@ extension EthereumNameService { let output = RegistryOutput(expectedResponsesCount: addresses.count) try await resolveRegistry(parameters: addresses.map(ENSRegistryResolverParameter.address), handler: { index, parameter, result in - guard let address = parameter.address else { return } + guard let address = parameter.address else { + return + } switch result { - case .success(let resolverAddress): + case let .success(resolverAddress): output.queries.append( ResolverQuery( index: index, @@ -107,7 +110,7 @@ extension EthereumNameService { nameHash: parameter.nameHash ) ) - case .failure(let error): + case let .failure(error): output.intermediaryResponses[index] = AddressResolveOutput( address: address, output: Self.output(from: error) @@ -123,9 +126,11 @@ extension EthereumNameService { let output = RegistryOutput(expectedResponsesCount: names.count) try await resolveRegistry(parameters: names.map(ENSRegistryResolverParameter.name), handler: { index, parameter, result in - guard let name = parameter.name else { return } + guard let name = parameter.name else { + return + } switch result { - case .success(let resolverAddress): + case let .success(resolverAddress): output.queries.append( ResolverQuery( index: index, @@ -134,7 +139,7 @@ extension EthereumNameService { nameHash: parameter.nameHash ) ) - case .failure(let error): + case let .failure(error): output.intermediaryResponses[index] = NameResolveOutput( ens: name, output: Self.output(from: error) @@ -145,9 +150,10 @@ extension EthereumNameService { return try await resolveQueries(registryOutput: output) } - private func resolveRegistry(parameters: [ENSRegistryResolverParameter], - handler: @escaping (Int, ENSRegistryResolverParameter, Result) -> Void) async throws { - + private func resolveRegistry( + parameters: [ENSRegistryResolverParameter], + handler: @escaping (Int, ENSRegistryResolverParameter, Result) -> Void + ) async throws { guard let network = client.network, let ensRegistryAddress = registryAddress ?? ENSContracts.registryAddress(for: network) else { throw EthereumNameServiceError.noNetwork } @@ -159,8 +165,10 @@ extension EthereumNameService { let function = ENSContracts.ENSRegistryFunctions.resolver(contract: ensRegistryAddress, parameter: parameter) - try aggegator.append(function: function, - response: ENSContracts.ENSRegistryResponses.RegistryResponse.self) { + try aggegator.append( + function: function, + response: ENSContracts.ENSRegistryResponses.RegistryResponse.self + ) { result in handler(index, parameter, result) } } @@ -180,13 +188,15 @@ extension EthereumNameService { registryOutput.queries.forEach { query in switch query.parameter { - case .address(let address): - guard let registryOutput = registryOutput as? RegistryOutput - else { fatalError("Invalid registry output provided") } + case let .address(address): + guard let registryOutput = registryOutput as? RegistryOutput else { + fatalError("Invalid registry output provided") + } resolveAddress(query, address: address, aggegator: &aggegator, registryOutput: registryOutput) - case .name(let name): - guard let registryOutput = registryOutput as? RegistryOutput - else { fatalError("Invalid registry output provided") } + case let .name(name): + guard let registryOutput = registryOutput as? RegistryOutput else { + fatalError("Invalid registry output provided") + } resolveName(query, ens: name, aggegator: &aggegator, registryOutput: registryOutput) } } @@ -211,19 +221,19 @@ extension EthereumNameService { response: ENSContracts.ENSRegistryResponses.AddressResolverResponse.self ) { result in switch result { - case .success(let name): + case let .success(name): registryOutput.intermediaryResponses[query.index] = AddressResolveOutput( address: address, output: .resolved(name) ) - case .failure(let error): + case let .failure(error): registryOutput.intermediaryResponses[query.index] = AddressResolveOutput( address: address, output: Self.output(from: error) ) } } - } catch let error { + } catch { registryOutput.intermediaryResponses[query.index] = AddressResolveOutput( address: address, output: Self.output(from: error) @@ -243,19 +253,19 @@ extension EthereumNameService { response: ENSContracts.ENSRegistryResponses.NameResolverResponse.self ) { result in switch result { - case .success(let address): + case let .success(address): registryOutput.intermediaryResponses[query.index] = NameResolveOutput( ens: ens, output: .resolved(address) ) - case .failure(let error): + case let .failure(error): registryOutput.intermediaryResponses[query.index] = NameResolveOutput( ens: ens, output: Self.output(from: error) ) } } - } catch let error { + } catch { registryOutput.intermediaryResponses[query.index] = NameResolveOutput( ens: ens, output: Self.output(from: error) @@ -264,13 +274,14 @@ extension EthereumNameService { } private static func output(from error: Error) -> ResolveOutput { - guard let error = error as? Multicall.CallError - else { return .couldNotBeResolved(.invalidInput) } + guard let error = error as? Multicall.CallError else { + return .couldNotBeResolved(.invalidInput) + } switch error { case .contractFailure: return .couldNotBeResolved(.invalidInput) - case .couldNotDecodeResponse(let error): + case let .couldNotDecodeResponse(error): if let specificError = error as? EthereumNameServiceError { return .couldNotBeResolved(specificError) } else { diff --git a/web3swift/src/ENS/ENSResolver.swift b/web3swift/src/ENS/ENSResolver.swift index 6a90f461..7c1c16c3 100644 --- a/web3swift/src/ENS/ENSResolver.swift +++ b/web3swift/src/ENS/ENSResolver.swift @@ -6,10 +6,9 @@ import Foundation class ENSResolver { - let address: EthereumAddress let callResolution: CallResolution - private (set) var supportsWildCard: Bool? + private(set) var supportsWildCard: Bool? private let client: EthereumClientProtocol @@ -37,7 +36,7 @@ class ENSResolver { } supportsWildCard = wildcardResolution - if mustSupportWildCard && !wildcardResolution { + if mustSupportWildCard, !wildcardResolution { // Wildcard name resolution (ENSIP-10) throw EthereumNameServiceError.ensUnknown } diff --git a/web3swift/src/ENS/EthereumNameService.swift b/web3swift/src/ENS/EthereumNameService.swift index 3e897ca4..98b7139e 100644 --- a/web3swift/src/ENS/EthereumNameService.swift +++ b/web3swift/src/ENS/EthereumNameService.swift @@ -12,12 +12,16 @@ public enum ResolutionMode { } protocol EthereumNameServiceProtocol { - func resolve(address: EthereumAddress, - mode: ResolutionMode, - completionHandler: @escaping(Result) -> Void) - func resolve(ens: String, - mode: ResolutionMode, - completionHandler: @escaping(Result) -> Void) + func resolve( + address: EthereumAddress, + mode: ResolutionMode, + completionHandler: @escaping (Result) -> Void + ) + func resolve( + ens: String, + mode: ResolutionMode, + completionHandler: @escaping (Result) -> Void + ) func resolve(address: EthereumAddress, mode: ResolutionMode) async throws -> String @@ -52,9 +56,11 @@ public class EthereumNameService: EthereumNameServiceProtocol { } } - required public init(client: EthereumClientProtocol, - registryAddress: EthereumAddress? = nil, - maximumRedirections: Int = 5) { + required public init( + client: EthereumClientProtocol, + registryAddress: EthereumAddress? = nil, + maximumRedirections: Int = 5 + ) { self.client = client self.registryAddress = registryAddress self.maximumRedirections = maximumRedirections @@ -67,13 +73,15 @@ public class EthereumNameService: EthereumNameServiceProtocol { } do { - let resolver = try await getResolver(for: address, - registryAddress: registryAddress, - mode: mode) + let resolver = try await getResolver( + for: address, + registryAddress: registryAddress, + mode: mode + ) let name = try await resolver.resolve(address: address) return name - } catch let error { + } catch { throw error as? EthereumNameServiceError ?? EthereumNameServiceError.ensUnknown } } @@ -84,14 +92,16 @@ public class EthereumNameService: EthereumNameServiceProtocol { throw EthereumNameServiceError.noNetwork } do { - let (resolver, supportingWildCard) = try await getResolver(for: ens, - fullName: ens, - registryAddress: registryAddress, - mode: mode) + let (resolver, supportingWildCard) = try await getResolver( + for: ens, + fullName: ens, + registryAddress: registryAddress, + mode: mode + ) let address = try await resolver.resolve(name: ens, supportingWildcard: supportingWildCard) return address - } catch let error { + } catch { throw error as? EthereumNameServiceError ?? EthereumNameServiceError.ensUnknown } } @@ -108,27 +118,31 @@ public class EthereumNameService: EthereumNameServiceProtocol { } extension EthereumNameService { - public func resolve(address: EthereumAddress, - mode: ResolutionMode, - completionHandler: @escaping(Result) -> Void) { + public func resolve( + address: EthereumAddress, + mode: ResolutionMode, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let name = try await resolve(address: address, mode: mode) completionHandler(.success(name)) - } catch let error { + } catch { completionHandler(.failure(error as? EthereumNameServiceError ?? .ensUnknown)) } } } - public func resolve(ens: String, - mode: ResolutionMode, - completionHandler: @escaping(Result) -> Void) { + public func resolve( + ens: String, + mode: ResolutionMode, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let address = try await resolve(ens: ens, mode: mode) completionHandler(.success(address)) - } catch let error { + } catch { completionHandler(.failure(error as? EthereumNameServiceError ?? .ensUnknown)) } } @@ -147,20 +161,26 @@ fileprivate extension ResolutionMode { } extension EthereumNameService { - private func getResolver(for address: EthereumAddress, - registryAddress: EthereumAddress, - mode: ResolutionMode) async throws -> ENSResolver { + private func getResolver( + for address: EthereumAddress, + registryAddress: EthereumAddress, + mode: ResolutionMode + ) async throws -> ENSResolver { let function = ENSContracts.ENSRegistryFunctions.resolver(contract: registryAddress, parameter: .address(address)) do { - let resolverAddress = try await function.call(withClient: client, - responseType: ENSContracts.AddressResponse.self, - block: .Latest, - resolution: .noOffchain(failOnExecutionError: true)).value - - let resolver = resolversByAddress[resolverAddress] ?? ENSResolver(address: resolverAddress, - client: client, - callResolution: mode.callResolution(maxRedirects: maximumRedirections)) + let resolverAddress = try await function.call( + withClient: client, + responseType: ENSContracts.AddressResponse.self, + block: .Latest, + resolution: .noOffchain(failOnExecutionError: true) + ).value + + let resolver = resolversByAddress[resolverAddress] ?? ENSResolver( + address: resolverAddress, + client: client, + callResolution: mode.callResolution(maxRedirects: maximumRedirections) + ) resolversByAddress[resolverAddress] = resolver return resolver } catch { @@ -168,17 +188,21 @@ extension EthereumNameService { } } - private func getResolver(for name: String, - fullName: String, - registryAddress: EthereumAddress, - mode: ResolutionMode) async throws -> (ENSResolver, Bool) { + private func getResolver( + for name: String, + fullName: String, + registryAddress: EthereumAddress, + mode: ResolutionMode + ) async throws -> (ENSResolver, Bool) { let function = ENSContracts.ENSRegistryFunctions.resolver(contract: registryAddress, parameter: .name(name)) do { - let resolverAddress = try await function.call(withClient: client, - responseType: ENSContracts.AddressResponse.self, - block: .Latest, - resolution: .noOffchain(failOnExecutionError: true)).value + let resolverAddress = try await function.call( + withClient: client, + responseType: ENSContracts.AddressResponse.self, + block: .Latest, + resolution: .noOffchain(failOnExecutionError: true) + ).value guard resolverAddress != .zero else { // Wildcard name resolution (ENSIP-10) @@ -189,15 +213,19 @@ extension EthereumNameService { } let parentName = parent.joined(separator: ".") - return try await getResolver(for: parentName, - fullName: fullName, - registryAddress: registryAddress, - mode: mode) + return try await getResolver( + for: parentName, + fullName: fullName, + registryAddress: registryAddress, + mode: mode + ) } - let resolver = resolversByAddress[resolverAddress] ?? ENSResolver(address: resolverAddress, - client: client, - callResolution: mode.callResolution(maxRedirects: maximumRedirections)) + let resolver = resolversByAddress[resolverAddress] ?? ENSResolver( + address: resolverAddress, + client: client, + callResolution: mode.callResolution(maxRedirects: maximumRedirections) + ) resolversByAddress[resolverAddress] = resolver return (resolver, fullName != name) } catch { diff --git a/web3swift/src/ERC1271/ERC1271.swift b/web3swift/src/ERC1271/ERC1271.swift index 6cbbb667..12f9336c 100644 --- a/web3swift/src/ERC1271/ERC1271.swift +++ b/web3swift/src/ERC1271/ERC1271.swift @@ -3,14 +3,14 @@ // Copyright © 2022 Argent Labs Limited. All rights reserved. // -import Foundation import BigInt +import Foundation public protocol ERC1271Protocol { init(client: EthereumClientProtocol) func isValidSignature(contract: EthereumAddress, messageHash: Data, signature: Data) async throws -> Bool - func isValidSignature(contract: EthereumAddress, messageHash: Data, signature: Data, completionHandler: @escaping(Result) -> Void) + func isValidSignature(contract: EthereumAddress, messageHash: Data, signature: Data, completionHandler: @escaping (Result) -> Void) } public class ERC1271: ERC1271Protocol { diff --git a/web3swift/src/ERC1271/ERC1271Functions.swift b/web3swift/src/ERC1271/ERC1271Functions.swift index 9b1bb433..37896e27 100644 --- a/web3swift/src/ERC1271/ERC1271Functions.swift +++ b/web3swift/src/ERC1271/ERC1271Functions.swift @@ -3,13 +3,11 @@ // Copyright © 2022 Argent Labs Limited. All rights reserved. // -import Foundation import BigInt +import Foundation public enum ERC1271Functions { - public struct isValidSignature: ABIFunction { - public static let name = "isValidSignature" public let gasPrice: BigUInt? = nil public let gasLimit: BigUInt? = nil @@ -19,10 +17,14 @@ public enum ERC1271Functions { public let message: Data public let signature: Data - public init(contract: EthereumAddress, - message: Data, - signature: Data) throws { - guard message.count == 32 && signature.count == 65 else { throw ERC1271Error.invalidInput } + public init( + contract: EthereumAddress, + message: Data, + signature: Data + ) throws { + guard message.count == 32, signature.count == 65 else { + throw ERC1271Error.invalidInput + } self.contract = contract self.message = message self.signature = signature diff --git a/web3swift/src/ERC1271/ERC1271Responses.swift b/web3swift/src/ERC1271/ERC1271Responses.swift index b6b83122..62f204a0 100644 --- a/web3swift/src/ERC1271/ERC1271Responses.swift +++ b/web3swift/src/ERC1271/ERC1271Responses.swift @@ -6,9 +6,7 @@ import Foundation public enum ERC1271Responses { - public struct isValidResponse: ABIResponse { - // bytes4(keccak256("isValidSignature(bytes32,bytes)") static let MAGICVALUE = Data(hex: "0x1626ba7e") @@ -22,29 +20,28 @@ public enum ERC1271Responses { // so we'll try parsing both types, though byte4 parsing is what's actually // on the finalised document. switch try values[0].decoded() as EitherBoolOrData4 { - case .bool(let bool): - self.isValid = bool - case .data(let data): - self.isValid = data == Self.MAGICVALUE + case let .bool(bool): + self.isValid = bool + case let .data(data): + self.isValid = data == Self.MAGICVALUE } } } // This will map the result to either a Bool value or a Data with 4 bytes private enum EitherBoolOrData4: ABIType { - // Both cases return 32 bytes of data public static var rawType: ABIRawType { .FixedBytes(32) } public static var parser: ParserFunction { - return { data in + { data in switch data.first ?? "" { - case "0x0000000000000000000000000000000000000000000000000000000000000000": - return EitherBoolOrData4.bool(false) - case "0x0000000000000000000000000000000000000000000000000000000000000001": - return EitherBoolOrData4.bool(true) - case let data: - return EitherBoolOrData4.data(try ABIDecoder.decode(data, to: Data.self).web3.bytes4) + case "0x0000000000000000000000000000000000000000000000000000000000000000": + return EitherBoolOrData4.bool(false) + case "0x0000000000000000000000000000000000000000000000000000000000000001": + return EitherBoolOrData4.bool(true) + case let data: + return EitherBoolOrData4.data(try ABIDecoder.decode(data, to: Data.self).web3.bytes4) } } } diff --git a/web3swift/src/ERC165/ERC165.swift b/web3swift/src/ERC165/ERC165.swift index 9ce69938..d3accd92 100644 --- a/web3swift/src/ERC165/ERC165.swift +++ b/web3swift/src/ERC165/ERC165.swift @@ -22,7 +22,7 @@ open class ERC165 { } extension ERC165 { - public func supportsInterface(contract: EthereumAddress, id: Data, completionHandler: @escaping(Result) -> Void) { + public func supportsInterface(contract: EthereumAddress, id: Data, completionHandler: @escaping (Result) -> Void) { Task { do { let result = try await supportsInterface(contract: contract, id: id) @@ -36,7 +36,7 @@ extension ERC165 { public enum ERC165Functions { public static var interfaceId: Data { - return "supportsInterface(bytes4)".web3.keccak256.web3.bytes4 + "supportsInterface(bytes4)".web3.keccak256.web3.bytes4 } struct supportsInterface: ABIFunction { @@ -48,11 +48,13 @@ public enum ERC165Functions { let interfaceId: Data - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - interfaceId: Data, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + interfaceId: Data, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.interfaceId = interfaceId @@ -69,7 +71,7 @@ public enum ERC165Functions { public enum ERC165Responses { public struct supportsInterfaceResponse: ABIResponse { - public static var types: [ABIType.Type] = [ Bool.self ] + public static var types: [ABIType.Type] = [Bool.self] public let supported: Bool public init?(values: [ABIDecoder.DecodedValue]) throws { diff --git a/web3swift/src/ERC20/ERC20.swift b/web3swift/src/ERC20/ERC20.swift index 963a4570..66c609ee 100644 --- a/web3swift/src/ERC20/ERC20.swift +++ b/web3swift/src/ERC20/ERC20.swift @@ -18,14 +18,13 @@ public protocol ERC20Protocol { func transferEventsFrom(sender: EthereumAddress, fromBlock: EthereumBlock, toBlock: EthereumBlock) async throws -> [ERC20Events.Transfer] // Deprecated - func name(tokenContract: EthereumAddress, completionHandler: @escaping(Result) -> Void) - func symbol(tokenContract: EthereumAddress, completionHandler: @escaping(Result) -> Void) - func decimals(tokenContract: EthereumAddress, completionHandler: @escaping(Result) -> Void) - func balanceOf(tokenContract: EthereumAddress, address: EthereumAddress, completionHandler: @escaping(Result) -> Void) - func allowance(tokenContract: EthereumAddress, address: EthereumAddress, spender: EthereumAddress, completionHandler: @escaping(Result) -> Void) - func transferEventsTo(recipient: EthereumAddress, fromBlock: EthereumBlock, toBlock: EthereumBlock, completionHandler: @escaping(Result<[ERC20Events.Transfer], Error>) -> Void) - func transferEventsFrom(sender: EthereumAddress, fromBlock: EthereumBlock, toBlock: EthereumBlock, completionHandler: @escaping(Result<[ERC20Events.Transfer], Error>) -> Void) - + func name(tokenContract: EthereumAddress, completionHandler: @escaping (Result) -> Void) + func symbol(tokenContract: EthereumAddress, completionHandler: @escaping (Result) -> Void) + func decimals(tokenContract: EthereumAddress, completionHandler: @escaping (Result) -> Void) + func balanceOf(tokenContract: EthereumAddress, address: EthereumAddress, completionHandler: @escaping (Result) -> Void) + func allowance(tokenContract: EthereumAddress, address: EthereumAddress, spender: EthereumAddress, completionHandler: @escaping (Result) -> Void) + func transferEventsTo(recipient: EthereumAddress, fromBlock: EthereumBlock, toBlock: EthereumBlock, completionHandler: @escaping (Result<[ERC20Events.Transfer], Error>) -> Void) + func transferEventsFrom(sender: EthereumAddress, fromBlock: EthereumBlock, toBlock: EthereumBlock, completionHandler: @escaping (Result<[ERC20Events.Transfer], Error>) -> Void) } open class ERC20: ERC20Protocol { @@ -49,9 +48,11 @@ open class ERC20: ERC20Protocol { public func decimals(tokenContract: EthereumAddress) async throws -> UInt8 { let function = ERC20Functions.decimals(contract: tokenContract) - let data = try await function.call(withClient: client, - responseType: ERC20Responses.decimalsResponse.self, - resolution: .noOffchain(failOnExecutionError: false)) + let data = try await function.call( + withClient: client, + responseType: ERC20Responses.decimalsResponse.self, + resolution: .noOffchain(failOnExecutionError: false) + ) return data.value } @@ -72,11 +73,13 @@ open class ERC20: ERC20Protocol { throw EthereumSignerError.unknownError } - let data = try await client.getEvents(addresses: nil, - topics: [ sig, nil, String(hexFromBytes: result)], - fromBlock: fromBlock, - toBlock: toBlock, - eventTypes: [ERC20Events.Transfer.self]) + let data = try await client.getEvents( + addresses: nil, + topics: [sig, nil, String(hexFromBytes: result)], + fromBlock: fromBlock, + toBlock: toBlock, + eventTypes: [ERC20Events.Transfer.self] + ) if let events = data.events as? [ERC20Events.Transfer] { return events @@ -90,16 +93,18 @@ open class ERC20: ERC20Protocol { throw EthereumSignerError.unknownError } - let data = try await client.getEvents(addresses: nil, - topics: [ sig, String(hexFromBytes: result), nil ], - fromBlock: fromBlock, - toBlock: toBlock, - eventTypes: [ERC20Events.Transfer.self]) + let data = try await client.getEvents( + addresses: nil, + topics: [sig, String(hexFromBytes: result), nil], + fromBlock: fromBlock, + toBlock: toBlock, + eventTypes: [ERC20Events.Transfer.self] + ) if let events = data.events as? [ERC20Events.Transfer] { - return events + return events } else { - throw EthereumClientError.decodeIssue + throw EthereumClientError.decodeIssue } } } diff --git a/web3swift/src/ERC20/ERC20Events.swift b/web3swift/src/ERC20/ERC20Events.swift index 8f7c9721..ad902bf2 100644 --- a/web3swift/src/ERC20/ERC20Events.swift +++ b/web3swift/src/ERC20/ERC20Events.swift @@ -9,7 +9,7 @@ import Foundation public enum ERC20Events { public struct Transfer: ABIEvent { public static let name = "Transfer" - public static let types: [ABIType.Type] = [ EthereumAddress.self, EthereumAddress.self, BigUInt.self] + public static let types: [ABIType.Type] = [EthereumAddress.self, EthereumAddress.self, BigUInt.self] public static let typesIndexed = [true, true, false] public let log: EthereumLog diff --git a/web3swift/src/ERC20/ERC20Functions.swift b/web3swift/src/ERC20/ERC20Functions.swift index a46c6223..451acabb 100644 --- a/web3swift/src/ERC20/ERC20Functions.swift +++ b/web3swift/src/ERC20/ERC20Functions.swift @@ -14,17 +14,19 @@ public enum ERC20Functions { public var contract: EthereumAddress public let from: EthereumAddress? - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.gasPrice = gasPrice self.gasLimit = gasLimit } - public func encode(to encoder: ABIFunctionEncoder) throws { } + public func encode(to encoder: ABIFunctionEncoder) throws {} } public struct symbol: ABIFunction { @@ -34,17 +36,19 @@ public enum ERC20Functions { public var contract: EthereumAddress public let from: EthereumAddress? - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.gasPrice = gasPrice self.gasLimit = gasLimit } - public func encode(to encoder: ABIFunctionEncoder) throws { } + public func encode(to encoder: ABIFunctionEncoder) throws {} } public struct decimals: ABIFunction { @@ -54,17 +58,19 @@ public enum ERC20Functions { public var contract: EthereumAddress public let from: EthereumAddress? - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.gasPrice = gasPrice self.gasLimit = gasLimit } - public func encode(to encoder: ABIFunctionEncoder) throws { } + public func encode(to encoder: ABIFunctionEncoder) throws {} } public struct balanceOf: ABIFunction { @@ -76,11 +82,13 @@ public enum ERC20Functions { public let account: EthereumAddress - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil, - account: EthereumAddress) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + account: EthereumAddress + ) { self.contract = contract self.from = from self.gasPrice = gasPrice @@ -103,12 +111,14 @@ public enum ERC20Functions { public let owner: EthereumAddress public let spender: EthereumAddress - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil, - owner: EthereumAddress, - spender: EthereumAddress) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + owner: EthereumAddress, + spender: EthereumAddress + ) { self.contract = contract self.from = from self.gasPrice = gasPrice @@ -133,12 +143,14 @@ public enum ERC20Functions { public let spender: EthereumAddress public let value: BigUInt - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil, - spender: EthereumAddress, - value: BigUInt) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + spender: EthereumAddress, + value: BigUInt + ) { self.contract = contract self.from = from self.gasPrice = gasPrice @@ -163,12 +175,14 @@ public enum ERC20Functions { public let to: EthereumAddress public let value: BigUInt - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil, - to: EthereumAddress, - value: BigUInt) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + to: EthereumAddress, + value: BigUInt + ) { self.contract = contract self.from = from self.gasPrice = gasPrice @@ -194,13 +208,15 @@ public enum ERC20Functions { public let to: EthereumAddress public let value: BigUInt - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil, - sender: EthereumAddress, - to: EthereumAddress, - value: BigUInt) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + sender: EthereumAddress, + to: EthereumAddress, + value: BigUInt + ) { self.contract = contract self.from = from self.gasPrice = gasPrice diff --git a/web3swift/src/ERC20/ERC20Responses.swift b/web3swift/src/ERC20/ERC20Responses.swift index a21d31e8..6ffd0fdc 100644 --- a/web3swift/src/ERC20/ERC20Responses.swift +++ b/web3swift/src/ERC20/ERC20Responses.swift @@ -8,7 +8,7 @@ import Foundation public enum ERC20Responses { public struct nameResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ String.self ] + public static var types: [ABIType.Type] = [String.self] public let value: String public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -17,7 +17,7 @@ public enum ERC20Responses { } public struct symbolResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ String.self ] + public static var types: [ABIType.Type] = [String.self] public let value: String public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -26,7 +26,7 @@ public enum ERC20Responses { } public struct decimalsResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ UInt8.self ] + public static var types: [ABIType.Type] = [UInt8.self] public let value: UInt8 public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -35,7 +35,7 @@ public enum ERC20Responses { } public struct balanceResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ BigUInt.self ] + public static var types: [ABIType.Type] = [BigUInt.self] public let value: BigUInt public init?(values: [ABIDecoder.DecodedValue]) throws { diff --git a/web3swift/src/ERC721/ERC721.swift b/web3swift/src/ERC721/ERC721.swift index d490cdca..fbcb9341 100644 --- a/web3swift/src/ERC721/ERC721.swift +++ b/web3swift/src/ERC721/ERC721.swift @@ -7,7 +7,7 @@ import BigInt import Foundation #if canImport(FoundationNetworking) -import FoundationNetworking + import FoundationNetworking #endif open class ERC721: ERC165 { @@ -30,11 +30,13 @@ open class ERC721: ERC165 { throw EthereumSignerError.unknownError } - let data = try await client.getEvents(addresses: nil, - topics: [ sig, nil, String(hexFromBytes: result)], - fromBlock: fromBlock, - toBlock: toBlock, - eventTypes: [ERC721Events.Transfer.self]) + let data = try await client.getEvents( + addresses: nil, + topics: [sig, nil, String(hexFromBytes: result)], + fromBlock: fromBlock, + toBlock: toBlock, + eventTypes: [ERC721Events.Transfer.self] + ) if let events = data.events as? [ERC721Events.Transfer] { return events @@ -48,11 +50,13 @@ open class ERC721: ERC165 { throw EthereumSignerError.unknownError } - let data = try await client.getEvents(addresses: nil, - topics: [ sig, String(hexFromBytes: result)], - fromBlock: fromBlock, - toBlock: toBlock, - eventTypes: [ERC721Events.Transfer.self]) + let data = try await client.getEvents( + addresses: nil, + topics: [sig, String(hexFromBytes: result)], + fromBlock: fromBlock, + toBlock: toBlock, + eventTypes: [ERC721Events.Transfer.self] + ) if let events = data.events as? [ERC721Events.Transfer] { return events @@ -78,9 +82,11 @@ public class ERC721Metadata: ERC721 { case fallback_property_name = "name" } - public init(title: String?, - type: String?, - properties: Properties?) { + public init( + title: String?, + type: String?, + properties: Properties? + ) { self.title = title self.type = type self.properties = properties @@ -100,9 +106,11 @@ public class ERC721Metadata: ERC721 { let image = try? container.decode(URL.self, forKey: .fallback_property_image) let description = try? container.decode(String.self, forKey: .fallback_property_description) if name != nil || image != nil || description != nil { - self.properties = Properties(name: Property(description: name), - description: Property(description: description), - image: Property(description: image)) + self.properties = Properties( + name: Property(description: name), + description: Property(description: description), + image: Property(description: image) + ) } else { self.properties = nil } @@ -170,7 +178,6 @@ public class ERC721Metadata: ERC721 { throw decodeError } } - } public class ERC721Enumerable: ERC721 { @@ -190,18 +197,22 @@ public class ERC721Enumerable: ERC721 { public func tokenOfOwnerByIndex(contract: EthereumAddress, owner: EthereumAddress, index: BigUInt) async throws -> BigUInt { let function = ERC721EnumerableFunctions.tokenOfOwnerByIndex(contract: contract, address: owner, index: index) - let data = try await function.call(withClient: client, - responseType: ERC721EnumerableResponses.numberResponse.self, - resolution: .noOffchain(failOnExecutionError: false)) + let data = try await function.call( + withClient: client, + responseType: ERC721EnumerableResponses.numberResponse.self, + resolution: .noOffchain(failOnExecutionError: false) + ) return data.value } } extension ERC721 { - public func balanceOf(contract: EthereumAddress, - address: EthereumAddress, - completionHandler: @escaping(Result) -> Void) { + public func balanceOf( + contract: EthereumAddress, + address: EthereumAddress, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let balance = try await balanceOf(contract: contract, address: address) @@ -212,9 +223,11 @@ extension ERC721 { } } - public func ownerOf(contract: EthereumAddress, - tokenId: BigUInt, - completionHandler: @escaping(Result) -> Void) { + public func ownerOf( + contract: EthereumAddress, + tokenId: BigUInt, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let ownerOf = try await ownerOf(contract: contract, tokenId: tokenId) @@ -225,10 +238,12 @@ extension ERC721 { } } - public func transferEventsTo(recipient: EthereumAddress, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - completionHandler: @escaping(Result<[ERC721Events.Transfer], Error>) -> Void) { + public func transferEventsTo( + recipient: EthereumAddress, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + completionHandler: @escaping (Result<[ERC721Events.Transfer], Error>) -> Void + ) { Task { do { let result = try await transferEventsTo(recipient: recipient, fromBlock: fromBlock, toBlock: toBlock) @@ -239,10 +254,12 @@ extension ERC721 { } } - public func transferEventsFrom(sender: EthereumAddress, - fromBlock: EthereumBlock, - toBlock: EthereumBlock, - completionHandler: @escaping(Result<[ERC721Events.Transfer], Error>) -> Void) { + public func transferEventsFrom( + sender: EthereumAddress, + fromBlock: EthereumBlock, + toBlock: EthereumBlock, + completionHandler: @escaping (Result<[ERC721Events.Transfer], Error>) -> Void + ) { Task { do { let result = try await transferEventsFrom(sender: sender, fromBlock: fromBlock, toBlock: toBlock) @@ -255,8 +272,10 @@ extension ERC721 { } extension ERC721Metadata { - public func name(contract: EthereumAddress, - completionHandler: @escaping(Result) -> Void) { + public func name( + contract: EthereumAddress, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let result = try await name(contract: contract) @@ -267,8 +286,10 @@ extension ERC721Metadata { } } - public func symbol(contract: EthereumAddress, - completionHandler: @escaping(Result) -> Void) { + public func symbol( + contract: EthereumAddress, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let result = try await symbol(contract: contract) @@ -279,9 +300,11 @@ extension ERC721Metadata { } } - public func tokenURI(contract: EthereumAddress, - tokenID: BigUInt, - completionHandler: @escaping(Result) -> Void) { + public func tokenURI( + contract: EthereumAddress, + tokenID: BigUInt, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let result = try await tokenURI(contract: contract, tokenID: tokenID) @@ -292,9 +315,11 @@ extension ERC721Metadata { } } - public func tokenMetadata(contract: EthereumAddress, - tokenID: BigUInt, - completionHandler: @escaping(Result) -> Void) { + public func tokenMetadata( + contract: EthereumAddress, + tokenID: BigUInt, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let result = try await tokenMetadata(contract: contract, tokenID: tokenID) @@ -307,8 +332,10 @@ extension ERC721Metadata { } extension ERC721Enumerable { - public func totalSupply(contract: EthereumAddress, - completionHandler: @escaping(Result) -> Void) { + public func totalSupply( + contract: EthereumAddress, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let result = try await totalSupply(contract: contract) @@ -319,9 +346,11 @@ extension ERC721Enumerable { } } - public func tokenByIndex(contract: EthereumAddress, - index: BigUInt, - completionHandler: @escaping(Result) -> Void) { + public func tokenByIndex( + contract: EthereumAddress, + index: BigUInt, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let result = try await tokenByIndex(contract: contract, index: index) @@ -332,10 +361,12 @@ extension ERC721Enumerable { } } - public func tokenOfOwnerByIndex(contract: EthereumAddress, - owner: EthereumAddress, - index: BigUInt, - completionHandler: @escaping(Result) -> Void) { + public func tokenOfOwnerByIndex( + contract: EthereumAddress, + owner: EthereumAddress, + index: BigUInt, + completionHandler: @escaping (Result) -> Void + ) { Task { do { let result = try await tokenOfOwnerByIndex(contract: contract, owner: owner, index: index) diff --git a/web3swift/src/ERC721/ERC721Events.swift b/web3swift/src/ERC721/ERC721Events.swift index 67484a65..cb794612 100644 --- a/web3swift/src/ERC721/ERC721Events.swift +++ b/web3swift/src/ERC721/ERC721Events.swift @@ -9,7 +9,7 @@ import Foundation public enum ERC721Events { public struct Transfer: ABIEvent { public static let name = "Transfer" - public static let types: [ABIType.Type] = [ EthereumAddress.self, EthereumAddress.self, BigUInt.self] + public static let types: [ABIType.Type] = [EthereumAddress.self, EthereumAddress.self, BigUInt.self] public static let typesIndexed = [true, true, true] public let log: EthereumLog @@ -29,7 +29,7 @@ public enum ERC721Events { public struct Approval: ABIEvent { public static let name = "Approval" - public static let types: [ABIType.Type] = [ EthereumAddress.self, EthereumAddress.self, BigUInt.self] + public static let types: [ABIType.Type] = [EthereumAddress.self, EthereumAddress.self, BigUInt.self] public static let typesIndexed = [true, true, true] public let log: EthereumLog @@ -49,7 +49,7 @@ public enum ERC721Events { public struct ApprovalForAll: ABIEvent { public static let name = "ApprovalForAll" - public static let types: [ABIType.Type] = [ EthereumAddress.self, EthereumAddress.self, BigUInt.self] + public static let types: [ABIType.Type] = [EthereumAddress.self, EthereumAddress.self, BigUInt.self] public static let typesIndexed = [true, true, true] public let log: EthereumLog diff --git a/web3swift/src/ERC721/ERC721Functions.swift b/web3swift/src/ERC721/ERC721Functions.swift index 12040874..5ef44dd9 100644 --- a/web3swift/src/ERC721/ERC721Functions.swift +++ b/web3swift/src/ERC721/ERC721Functions.swift @@ -8,7 +8,7 @@ import Foundation public enum ERC721Functions { public static var interfaceId: Data { - return "0x80ac58cd".web3.hexData! + "0x80ac58cd".web3.hexData! } public struct balanceOf: ABIFunction { @@ -20,11 +20,13 @@ public enum ERC721Functions { public let owner: EthereumAddress - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - owner: EthereumAddress, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + owner: EthereumAddress, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.owner = owner @@ -46,11 +48,13 @@ public enum ERC721Functions { public let tokenId: BigUInt - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - tokenId: BigUInt, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + tokenId: BigUInt, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.tokenId = tokenId @@ -74,13 +78,15 @@ public enum ERC721Functions { public let to: EthereumAddress public let tokenId: BigUInt - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil, - sender: EthereumAddress, - to: EthereumAddress, - tokenId: BigUInt) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + sender: EthereumAddress, + to: EthereumAddress, + tokenId: BigUInt + ) { self.contract = contract self.from = from self.gasPrice = gasPrice @@ -108,13 +114,15 @@ public enum ERC721Functions { public let to: EthereumAddress public let tokenId: BigUInt - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil, - sender: EthereumAddress, - to: EthereumAddress, - tokenId: BigUInt) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + sender: EthereumAddress, + to: EthereumAddress, + tokenId: BigUInt + ) { self.contract = contract self.from = from self.gasPrice = gasPrice @@ -143,14 +151,16 @@ public enum ERC721Functions { public let tokenId: BigUInt public let data: Data - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil, - sender: EthereumAddress, - to: EthereumAddress, - tokenId: BigUInt, - data: Data) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + sender: EthereumAddress, + to: EthereumAddress, + tokenId: BigUInt, + data: Data + ) { self.contract = contract self.from = from self.gasPrice = gasPrice @@ -172,7 +182,7 @@ public enum ERC721Functions { public enum ERC721MetadataFunctions { public static var interfaceId: Data { - return "name()".web3.keccak256.web3.bytes4 ^ + "name()".web3.keccak256.web3.bytes4 ^ "symbol()".web3.keccak256.web3.bytes4 ^ "tokenURI(uint256)".web3.keccak256.web3.bytes4 } @@ -184,17 +194,19 @@ public enum ERC721MetadataFunctions { public var contract: EthereumAddress public let from: EthereumAddress? - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.gasPrice = gasPrice self.gasLimit = gasLimit } - public func encode(to encoder: ABIFunctionEncoder) throws { } + public func encode(to encoder: ABIFunctionEncoder) throws {} } public struct symbol: ABIFunction { @@ -204,17 +216,19 @@ public enum ERC721MetadataFunctions { public var contract: EthereumAddress public let from: EthereumAddress? - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.gasPrice = gasPrice self.gasLimit = gasLimit } - public func encode(to encoder: ABIFunctionEncoder) throws { } + public func encode(to encoder: ABIFunctionEncoder) throws {} } public struct tokenURI: ABIFunction { @@ -226,11 +240,13 @@ public enum ERC721MetadataFunctions { public let tokenID: BigUInt - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - tokenID: BigUInt, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + tokenID: BigUInt, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.tokenID = tokenID @@ -246,7 +262,7 @@ public enum ERC721MetadataFunctions { public enum ERC721EnumerableFunctions { public static var interfaceId: Data { - return "totalSupply()".web3.keccak256.web3.bytes4 ^ + "totalSupply()".web3.keccak256.web3.bytes4 ^ "tokenByIndex(uint256)".web3.keccak256.web3.bytes4 ^ "tokenOfOwnerByIndex(address,uint256)".web3.keccak256.web3.bytes4 } @@ -258,17 +274,19 @@ public enum ERC721EnumerableFunctions { public var contract: EthereumAddress public let from: EthereumAddress? - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.gasPrice = gasPrice self.gasLimit = gasLimit } - public func encode(to encoder: ABIFunctionEncoder) throws { } + public func encode(to encoder: ABIFunctionEncoder) throws {} } public struct tokenByIndex: ABIFunction { @@ -280,11 +298,13 @@ public enum ERC721EnumerableFunctions { public let index: BigUInt - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - index: BigUInt, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + index: BigUInt, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.index = index @@ -307,12 +327,14 @@ public enum ERC721EnumerableFunctions { public let address: EthereumAddress public let index: BigUInt - public init(contract: EthereumAddress, - from: EthereumAddress? = nil, - address: EthereumAddress, - index: BigUInt, - gasPrice: BigUInt? = nil, - gasLimit: BigUInt? = nil) { + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + address: EthereumAddress, + index: BigUInt, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil + ) { self.contract = contract self.from = from self.address = address diff --git a/web3swift/src/ERC721/ERC721Responses.swift b/web3swift/src/ERC721/ERC721Responses.swift index 6ec56f7f..0886e143 100644 --- a/web3swift/src/ERC721/ERC721Responses.swift +++ b/web3swift/src/ERC721/ERC721Responses.swift @@ -8,7 +8,7 @@ import Foundation public enum ERC721Responses { public struct balanceResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ BigUInt.self ] + public static var types: [ABIType.Type] = [BigUInt.self] public let value: BigUInt public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -17,7 +17,7 @@ public enum ERC721Responses { } public struct ownerResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ EthereumAddress.self ] + public static var types: [ABIType.Type] = [EthereumAddress.self] public let value: EthereumAddress public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -28,7 +28,7 @@ public enum ERC721Responses { public enum ERC721MetadataResponses { public struct nameResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ String.self ] + public static var types: [ABIType.Type] = [String.self] public let value: String public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -37,7 +37,7 @@ public enum ERC721MetadataResponses { } public struct symbolResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ String.self ] + public static var types: [ABIType.Type] = [String.self] public let value: String public init?(values: [ABIDecoder.DecodedValue]) throws { @@ -46,10 +46,9 @@ public enum ERC721MetadataResponses { } public struct tokenURIResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ URL.self ] + public static var types: [ABIType.Type] = [URL.self] - @available(*, deprecated, renamed: "value") - public var uri: URL { value } + @available(*, deprecated, renamed: "value") public var uri: URL { value } public let value: URL @@ -61,7 +60,7 @@ public enum ERC721MetadataResponses { public enum ERC721EnumerableResponses { public struct numberResponse: ABIResponse, MulticallDecodableResponse { - public static var types: [ABIType.Type] = [ BigUInt.self ] + public static var types: [ABIType.Type] = [BigUInt.self] public let value: BigUInt public init?(values: [ABIDecoder.DecodedValue]) throws { diff --git a/web3swift/src/Extensions/ByteExtensions.swift b/web3swift/src/Extensions/ByteExtensions.swift index 8b96eb5d..b951c8bb 100644 --- a/web3swift/src/Extensions/ByteExtensions.swift +++ b/web3swift/src/Extensions/ByteExtensions.swift @@ -11,13 +11,13 @@ public extension Web3Extensions where Base == BigUInt { let data = base.magnitude.serialize() let bytes = data.web3.bytes let lastIndex = bytes.count - 1 - let firstIndex = bytes.firstIndex(where: {$0 != 0x00}) ?? lastIndex + let firstIndex = bytes.firstIndex(where: { $0 != 0x00 }) ?? lastIndex if lastIndex < 0 { return Array([0]) } - return Array(bytes[firstIndex...lastIndex]) + return Array(bytes[firstIndex ... lastIndex]) } } @@ -60,20 +60,20 @@ public extension Web3Extensions where Base == BigInt { let bytes = data.web3.bytes let lastIndex = bytes.count - 1 - let firstIndex = bytes.firstIndex(where: {$0 != 0x00}) ?? lastIndex + let firstIndex = bytes.firstIndex(where: { $0 != 0x00 }) ?? lastIndex if lastIndex < 0 { return Array([0]) } - return Array(bytes[firstIndex...lastIndex]) + return Array(bytes[firstIndex ... lastIndex]) } } public extension Data { static func ^ (lhs: Data, rhs: Data) -> Data { let bytes = zip(lhs.web3.bytes, rhs.web3.bytes).map { lhsByte, rhsByte in - return lhsByte ^ rhsByte + lhsByte ^ rhsByte } return Data(bytes) @@ -82,7 +82,7 @@ public extension Data { public extension Web3Extensions where Base == Data { var bytes: [UInt8] { - return Array(base) + Array(base) } var strippingZeroesFromBytes: Data { @@ -90,15 +90,15 @@ public extension Web3Extensions where Base == Data { while bytes.first == 0 { bytes.removeFirst() } - return Data.init(bytes) + return Data(bytes) } var bytes4: Data { - return base.prefix(4) + base.prefix(4) } var bytes32: Data { - return base.prefix(32) + base.prefix(32) } } @@ -110,7 +110,7 @@ public extension String { public extension Web3Extensions where Base == String { var bytes: [UInt8] { - return [UInt8](base.utf8) + [UInt8](base.utf8) } var bytesFromHex: [UInt8]? { diff --git a/web3swift/src/Extensions/Data+Random.swift b/web3swift/src/Extensions/Data+Random.swift index 8ef5187a..c01cdfcc 100644 --- a/web3swift/src/Extensions/Data+Random.swift +++ b/web3swift/src/Extensions/Data+Random.swift @@ -7,6 +7,6 @@ import Foundation extension Data { static func randomOfLength(_ length: Int) -> Data? { - return Data((0 ..< length).map { _ in UInt8.random(in: UInt8.min ... UInt8.max) }) + Data((0 ..< length).map { _ in UInt8.random(in: UInt8.min ... UInt8.max) }) } } diff --git a/web3swift/src/Extensions/Extensions.swift b/web3swift/src/Extensions/Extensions.swift index 1dd1fc29..8eabc16b 100644 --- a/web3swift/src/Extensions/Extensions.swift +++ b/web3swift/src/Extensions/Extensions.swift @@ -13,7 +13,7 @@ public protocol Web3Extendable { public extension Web3Extendable { var web3: Web3Extensions { - return Web3Extensions(self) + Web3Extensions(self) } } diff --git a/web3swift/src/Extensions/HexExtensions.swift b/web3swift/src/Extensions/HexExtensions.swift index 6fc5c86e..8d38f292 100644 --- a/web3swift/src/Extensions/HexExtensions.swift +++ b/web3swift/src/Extensions/HexExtensions.swift @@ -14,11 +14,11 @@ public extension BigUInt { public extension Web3Extensions where Base == BigUInt { var hexString: String { - return String(bytes: base.web3.bytes) + String(bytes: base.web3.bytes) } var hexStringNoLeadingZeroes: String { - return base.web3.hexString.replacingOccurrences(of: "(?<=0x)0+", with: "", options: .regularExpression) + base.web3.hexString.replacingOccurrences(of: "(?<=0x)0+", with: "", options: .regularExpression) } } @@ -36,7 +36,7 @@ public extension Int { public extension Web3Extensions where Base == Int { var hexString: String { - return "0x" + String(format: "%x", base) + "0x" + String(format: "%x", base) } } diff --git a/web3swift/src/Extensions/ResultExtensions.swift b/web3swift/src/Extensions/ResultExtensions.swift index 5e7f0501..33936421 100644 --- a/web3swift/src/Extensions/ResultExtensions.swift +++ b/web3swift/src/Extensions/ResultExtensions.swift @@ -14,9 +14,9 @@ extension Result where Failure == JSONRPCError { } } - func tryMap(_ transform: (Success) throws -> NewSuccess) -> Result { - flatMap { value in - Result { try transform(value) } - } - } + func tryMap(_ transform: (Success) throws -> NewSuccess) -> Result { + flatMap { value in + Result { try transform(value) } + } + } } diff --git a/web3swift/src/Extensions/String+Numeric.swift b/web3swift/src/Extensions/String+Numeric.swift index 3fd6f746..1f7e3657 100644 --- a/web3swift/src/Extensions/String+Numeric.swift +++ b/web3swift/src/Extensions/String+Numeric.swift @@ -17,12 +17,12 @@ public extension Web3Extensions where Base == String { return base.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil } - + var isAddress: Bool { - let hexAddressPattern = #"(?=^0x[a-fA-F0-9]{40}$)"# - return checkRegexMatching(pattern: hexAddressPattern) + let hexAddressPattern = #"(?=^0x[a-fA-F0-9]{40}$)"# + return checkRegexMatching(pattern: hexAddressPattern) } - + private func checkRegexMatching(pattern: String) -> Bool { do { let passwordRegex = try NSRegularExpression( @@ -30,7 +30,7 @@ public extension Web3Extensions where Base == String { options: [] ) let sourceRange = NSRange( - self.base.startIndex.. String { - guard let data = "\(message)".data(using: .utf8) else { throw EthereumAccountError.signError } + guard let data = "\(message)".data(using: .utf8) else { + throw EthereumAccountError.signError + } return try signMessage(message: data) } } diff --git a/web3swift/src/SIWE/SiweMessage+Codable.swift b/web3swift/src/SIWE/SiweMessage+Codable.swift index 156a4d25..ce741de9 100644 --- a/web3swift/src/SIWE/SiweMessage+Codable.swift +++ b/web3swift/src/SIWE/SiweMessage+Codable.swift @@ -6,7 +6,6 @@ import Foundation extension SiweMessage: Codable { - enum CodingKeys: String, CaseIterable, CodingKey { case domain case address diff --git a/web3swift/src/SIWE/SiweMessage+RegEx.swift b/web3swift/src/SIWE/SiweMessage+RegEx.swift index 434a7b67..e58765b7 100644 --- a/web3swift/src/SIWE/SiweMessage+RegEx.swift +++ b/web3swift/src/SIWE/SiweMessage+RegEx.swift @@ -6,10 +6,8 @@ import Foundation extension SiweMessage { - /// Errors thrown while trying to parse a SIWE string message using RegEx enum RegExError: Swift.Error { - /// Error thrown when no absolute matches were found in the message case noMatches /// Error thrown in case we couldn't create a JSON object from parsed values @@ -20,7 +18,6 @@ extension SiweMessage { /// /// Check [the docs web page](https://docs.login.xyz) for more info private enum RegEx { - static let domain = "(?<\(CodingKeys.domain.rawValue)>([^?#]*)) wants you to sign in with your Ethereum account:" static let address = "\n(?<\(CodingKeys.address.rawValue)>0x[a-zA-Z0-9]{40})\n\n" static let statement = "((?<\(CodingKeys.statement.rawValue)>[^\n]+)\n)?" @@ -44,7 +41,9 @@ extension SiweMessage { throw RegExError.noMatches } let range = NSRange(string.startIndex ..< string.endIndex, in: string) - guard let match = regex.firstMatch(in: string, options: [], range: range) else { throw RegExError.noMatches } + guard let match = regex.firstMatch(in: string, options: [], range: range) else { + throw RegExError.noMatches + } var messageDict: [String: Any] = [:] for field in CodingKeys.allCases { @@ -64,7 +63,9 @@ extension SiweMessage { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .formatted(SiweMessage.dateFormatter) - guard let jsonData = try? JSONSerialization.data(withJSONObject: messageDict, options: []) else { throw RegExError.invalidJson } + guard let jsonData = try? JSONSerialization.data(withJSONObject: messageDict, options: []) else { + throw RegExError.invalidJson + } self = try decoder.decode(SiweMessage.self, from: jsonData) } diff --git a/web3swift/src/SIWE/SiweMessage+String.swift b/web3swift/src/SIWE/SiweMessage+String.swift index bea39720..8de8c5d9 100644 --- a/web3swift/src/SIWE/SiweMessage+String.swift +++ b/web3swift/src/SIWE/SiweMessage+String.swift @@ -6,7 +6,6 @@ import Foundation extension SiweMessage: CustomStringConvertible { - static let dateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.timeZone = TimeZone(identifier: "UTC") diff --git a/web3swift/src/SIWE/SiweMessage+Validation.swift b/web3swift/src/SIWE/SiweMessage+Validation.swift index c7a62400..e01314f0 100644 --- a/web3swift/src/SIWE/SiweMessage+Validation.swift +++ b/web3swift/src/SIWE/SiweMessage+Validation.swift @@ -6,7 +6,6 @@ import Foundation extension SiweMessage { - /// Errors thrown when checking if a `SiweMessage` is valid or not public enum ValidationError: Swift.Error { /// The domain provided is not valid, should be a host name @@ -24,34 +23,41 @@ extension SiweMessage { } func validate() throws { - guard !domain.isEmpty, - domain.matches(regex: "[^#?]*") - else { throw ValidationError.invalidDomain } + domain.matches(regex: "[^#?]*") else { + throw ValidationError.invalidDomain + } - guard address.isEIP55Address else { throw ValidationError.invalidAddress } + guard address.isEIP55Address else { + throw ValidationError.invalidAddress + } - guard version == "1" else { throw ValidationError.invalidVersion } + guard version == "1" else { + throw ValidationError.invalidVersion + } - guard chainId > 0 else { throw ValidationError.invalidChainId } + guard chainId > 0 else { + throw ValidationError.invalidChainId + } guard nonce.count >= 8, - nonce.matches(regex: "[a-zA-Z0-9]{8,}") - else { throw ValidationError.invalidNonce } + nonce.matches(regex: "[a-zA-Z0-9]{8,}") else { + throw ValidationError.invalidNonce + } if let requestId = requestId { guard !requestId.isEmpty, - requestId.matches(regex: "[-._~!$&'()*+,;=:@%a-zA-Z0-9]*") - else { throw ValidationError.invalidRequestId } + requestId.matches(regex: "[-._~!$&'()*+,;=:@%a-zA-Z0-9]*") else { + throw ValidationError.invalidRequestId + } } } } private extension String { - func matches(regex: String) -> Bool { let match = range(of: regex, options: .regularExpression, range: startIndex ..< endIndex, locale: Locale(identifier: "en_US_POSIX")) return match == startIndex ..< endIndex diff --git a/web3swift/src/SIWE/SiweMessage.swift b/web3swift/src/SIWE/SiweMessage.swift index 463977a1..4ab04e7e 100644 --- a/web3swift/src/SIWE/SiweMessage.swift +++ b/web3swift/src/SIWE/SiweMessage.swift @@ -9,7 +9,6 @@ import Foundation /// /// For more information on SIWE check out https://docs.login.xyz public struct SiweMessage: Hashable { - /// RFC 4501 dns authority that is requesting the signing. public var domain: String diff --git a/web3swift/src/SIWE/SiweVerifier.swift b/web3swift/src/SIWE/SiweVerifier.swift index a445a390..c759e99b 100644 --- a/web3swift/src/SIWE/SiweVerifier.swift +++ b/web3swift/src/SIWE/SiweVerifier.swift @@ -3,12 +3,11 @@ // Copyright © 2022 Argent Labs Limited. All rights reserved. // -import Foundation import BigInt +import Foundation /// An object which will verify if a given `SiweMessage` and signature match with the EVM address provided public class SiweVerifier { - /// Errors thrown when verifing a given message agains a signature public enum Error: Swift.Error { /// The provided message is from a different network than the client's. @@ -38,7 +37,7 @@ public class SiweVerifier { /// - Returns: a `Bool` indicating if the pair message-signature is verified (whether or not the signature came from the address in the message) /// - Throws: any errors thrown from `SiweMessage.init(_:)` and `SiweVerifier.verify(message:against)` public final func verify(_ message: String, against signature: String) async throws -> Bool { - return try await verify(message: SiweMessage(message), against: signature) + try await verify(message: SiweMessage(message), against: signature) } /// Verifies if a given `SiweMessage` was signed by the address in the message. @@ -63,9 +62,13 @@ public class SiweVerifier { } } - guard message.chainId == client.network?.intValue else { throw Error.differentNetwork } + guard message.chainId == client.network?.intValue else { + throw Error.differentNetwork + } - guard let messageData = "\(message)".data(using: .utf8) else { throw Error.invalidMessageData } + guard let messageData = "\(message)".data(using: .utf8) else { + throw Error.invalidMessageData + } let prefix = "\u{19}Ethereum Signed Message:\n\(String(messageData.count))" guard let prefixData = prefix.data(using: .ascii) else { assertionFailure("Etherem personal message signature prefix is not valid") @@ -73,7 +76,9 @@ public class SiweVerifier { } let messageHash = (prefixData + messageData).web3.keccak256 - guard let signatureData = signature.web3.hexData else { throw Error.invalidSignature } + guard let signatureData = signature.web3.hexData else { + throw Error.invalidSignature + } let address = EthereumAddress(try KeyUtil.recoverPublicKey(message: messageHash, signature: signatureData)) if address.toChecksumAddress() == message.address { diff --git a/web3swift/src/Utils/HexUtil.swift b/web3swift/src/Utils/HexUtil.swift index 6322051a..451b5eb7 100644 --- a/web3swift/src/Utils/HexUtil.swift +++ b/web3swift/src/Utils/HexUtil.swift @@ -11,17 +11,15 @@ enum HexConversionError: Error { } class HexUtil { - private static func convert(hexDigit digit: UnicodeScalar) throws -> UInt8 { switch digit { - - case UnicodeScalar(unicodeScalarLiteral: "0")...UnicodeScalar(unicodeScalarLiteral: "9"): + case UnicodeScalar(unicodeScalarLiteral: "0") ... UnicodeScalar(unicodeScalarLiteral: "9"): return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral: "0").value) - case UnicodeScalar(unicodeScalarLiteral: "a")...UnicodeScalar(unicodeScalarLiteral: "f"): + case UnicodeScalar(unicodeScalarLiteral: "a") ... UnicodeScalar(unicodeScalarLiteral: "f"): return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral: "a").value + 0xa) - case UnicodeScalar(unicodeScalarLiteral: "A")...UnicodeScalar(unicodeScalarLiteral: "F"): + case UnicodeScalar(unicodeScalarLiteral: "A") ... UnicodeScalar(unicodeScalarLiteral: "F"): return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral: "A").value + 0xa) default: @@ -38,7 +36,7 @@ class HexUtil { do { let convertedMsn = try convert(hexDigit: msn) let convertedLsn = try convert(hexDigit: lsn) - byteArray += [ convertedMsn << 4 | convertedLsn ] + byteArray += [convertedMsn << 4 | convertedLsn] } catch { throw error } diff --git a/web3swift/src/Utils/KeyDerivation.swift b/web3swift/src/Utils/KeyDerivation.swift index 17cafe0d..7b7b45d3 100644 --- a/web3swift/src/Utils/KeyDerivation.swift +++ b/web3swift/src/Utils/KeyDerivation.swift @@ -5,10 +5,10 @@ import Foundation #if canImport(CommonCrypto) -import CommonCrypto + import CommonCrypto #endif #if !COCOAPODS -import Internal_CryptoSwift_PBDKF2 + import Internal_CryptoSwift_PBDKF2 #endif enum KeyDerivationAlgorithm { @@ -16,14 +16,14 @@ enum KeyDerivationAlgorithm { case pbkdf2sha512 #if canImport(CommonCrypto) - func ccAlgorithm() -> CCAlgorithm { - switch self { - case .pbkdf2sha256: - return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) - case .pbkdf2sha512: - return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) + func ccAlgorithm() -> CCAlgorithm { + switch self { + case .pbkdf2sha256: + return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) + case .pbkdf2sha512: + return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) + } } - } #endif func function() -> String { @@ -55,7 +55,6 @@ enum KeyDerivationAlgorithm { } class KeyDerivator { - var algorithm: KeyDerivationAlgorithm var dklen: Int var round: Int @@ -67,7 +66,6 @@ class KeyDerivator { } func deriveKey(key: String, salt: Data, forceCryptoSwiftImplementation: Bool = false) -> Data? { - let password = key let keyByteCount = dklen let rounds = round @@ -78,16 +76,17 @@ class KeyDerivator { } #if canImport(CommonCrypto) - return pbkdf2(hash: algorithm.ccAlgorithm(), password: password, salt: salt, keyByteCount: keyByteCount, rounds: rounds) + return pbkdf2(hash: algorithm.ccAlgorithm(), password: password, salt: salt, keyByteCount: keyByteCount, rounds: rounds) #else - return pbkdf2(variant: algorithm.hmacVariant(), password: password, salt: salt, keyByteCount: keyByteCount, rounds: rounds) + return pbkdf2(variant: algorithm.hmacVariant(), password: password, salt: salt, keyByteCount: keyByteCount, rounds: rounds) #endif } func deriveKey(key: String, salt: String, forceCryptoSwiftImplementation: Bool = false) -> Data? { - let password = key - guard let saltData = salt.data(using: .utf8) else { return nil } + guard let saltData = salt.data(using: .utf8) else { + return nil + } let keyByteCount = dklen let rounds = round @@ -97,40 +96,43 @@ class KeyDerivator { } #if canImport(CommonCrypto) - return pbkdf2(hash: algorithm.ccAlgorithm(), password: password, salt: saltData, keyByteCount: keyByteCount, rounds: rounds) + return pbkdf2(hash: algorithm.ccAlgorithm(), password: password, salt: saltData, keyByteCount: keyByteCount, rounds: rounds) #else - return pbkdf2(variant: algorithm.hmacVariant(), password: password, salt: saltData, keyByteCount: keyByteCount, rounds: rounds) + return pbkdf2(variant: algorithm.hmacVariant(), password: password, salt: saltData, keyByteCount: keyByteCount, rounds: rounds) #endif - } #if canImport(CommonCrypto) - private func pbkdf2(hash: CCPBKDFAlgorithm, password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? { - guard let passwordData = password.data(using: String.Encoding.utf8) else { return nil } - var derivedKeyData = [UInt8](repeating: 0, count: keyByteCount) - var saltData = salt.web3.bytes - let derivationStatus = CCKeyDerivationPBKDF( - CCPBKDFAlgorithm(kCCPBKDF2), - password, - passwordData.count, - &saltData, - saltData.count, - hash, - UInt32(rounds), - &derivedKeyData, - derivedKeyData.count) - - if derivationStatus != 0 { - print("Error: \(derivationStatus)") - return nil + private func pbkdf2(hash: CCPBKDFAlgorithm, password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? { + guard let passwordData = password.data(using: String.Encoding.utf8) else { + return nil + } + var derivedKeyData = [UInt8](repeating: 0, count: keyByteCount) + var saltData = salt.web3.bytes + let derivationStatus = CCKeyDerivationPBKDF( + CCPBKDFAlgorithm(kCCPBKDF2), + password, + passwordData.count, + &saltData, + saltData.count, + hash, + UInt32(rounds), + &derivedKeyData, + derivedKeyData.count + ) + + if derivationStatus != 0 { + print("Error: \(derivationStatus)") + return nil + } + return Data(derivedKeyData) } - return Data(derivedKeyData) - } #endif private func pbkdf2(variant: HMAC.Variant, password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? { - - guard let passwordData = password.data(using: String.Encoding.utf8) else { return nil } + guard let passwordData = password.data(using: String.Encoding.utf8) else { + return nil + } let derivedKey = try? PBKDF2( password: [UInt8](passwordData), @@ -142,5 +144,4 @@ class KeyDerivator { return derivedKey.map { Data($0) } } - } diff --git a/web3swift/src/Utils/KeyUtil.swift b/web3swift/src/Utils/KeyUtil.swift index e000f147..6b41f126 100644 --- a/web3swift/src/Utils/KeyUtil.swift +++ b/web3swift/src/Utils/KeyUtil.swift @@ -3,9 +3,9 @@ // Copyright © 2022 Argent Labs Limited. All rights reserved. // -import Foundation import Logging import secp256k1 +import Foundation enum KeyUtilError: Error { case invalidContext @@ -22,7 +22,7 @@ class KeyUtil { } static func generatePrivateKeyData() -> Data? { - return Data.randomOfLength(32) + Data.randomOfLength(32) } static func generatePublicKey(from privateKey: Data) throws -> Data { @@ -57,14 +57,14 @@ class KeyUtil { } secp256k1_ec_pubkey_serialize(ctx, outputPtr, &publicKeyLength, publicKeyPtr, UInt32(SECP256K1_EC_UNCOMPRESSED)) - let publicKey = Data(bytes: outputPtr, count: publicKeyLength).subdata(in: 1.. EthereumAddress { let hash = publicKey.web3.keccak256 - let address = hash.subdata(in: 12...allocate(capacity: 1) defer { signaturePtr.deallocate() } - let serializedSignature = Data(signature[0..<64]) + let serializedSignature = Data(signature[0 ..< 64]) var v = Int32(signature[64]) - if v >= 27 && v <= 30 { + if v >= 27, v <= 30 { v -= 27 - } else if v >= 31 && v <= 34 { + } else if v >= 31, v <= 34 { v -= 31 - } else if v >= 35 && v <= 38 { + } else if v >= 35, v <= 38 { v -= 35 } @@ -148,11 +148,10 @@ class KeyUtil { throw KeyUtilError.signatureFailure } } - var size: Int = 65 + var size = 65 var rv = Data(count: size) rv.withUnsafeMutableBytes { secp256k1_ec_pubkey_serialize(ctx, $0.bindMemory(to: UInt8.self).baseAddress!, &size, pubkey, UInt32(SECP256K1_EC_UNCOMPRESSED)) - return } return "0x\(rv[1...].web3.keccak256.web3.hexString.suffix(40))" } diff --git a/web3swift/src/Utils/KeystoreUtil.swift b/web3swift/src/Utils/KeystoreUtil.swift index ccb14083..b36a1a80 100644 --- a/web3swift/src/Utils/KeystoreUtil.swift +++ b/web3swift/src/Utils/KeystoreUtil.swift @@ -34,7 +34,6 @@ class KeystoreUtil: KeystoreUtilProtocol { } static func encode(privateKey: Data, password: String, salt: Data, iv: Data) throws -> Data { - // derive address from private key let publicKeyData = try KeyUtil.generatePublicKey(from: privateKey) let address = KeyUtil.generateAddress(from: publicKeyData) @@ -46,12 +45,12 @@ class KeystoreUtil: KeystoreUtilProtocol { } // encrypt private key - let encKey = derivedKey.subdata(in: 0..<16) + let encKey = derivedKey.subdata(in: 0 ..< 16) let encryptor = Aes128Util(key: encKey, iv: iv) let ciphertext = encryptor.xcrypt(input: privateKey) // compute mac - let macKey = derivedKey.subdata(in: 16..<32) + let macKey = derivedKey.subdata(in: 16 ..< 32) let concat = macKey + ciphertext let mac = concat.web3.keccak256 @@ -62,7 +61,8 @@ class KeystoreUtil: KeystoreUtilProtocol { ciphertext: ciphertext.web3.hexString.web3.noHexPrefix, kdf: keyDerivator.algorithm.function(), kdfparams: KeystoreFileCryptoKdfParams(c: dkround, dklen: dklen, prf: keyDerivator.algorithm.hash(), salt: salt.web3.hexString.web3.noHexPrefix), - mac: mac.web3.hexString.web3.noHexPrefix) + mac: mac.web3.hexString.web3.noHexPrefix + ) let keystore = KeystoreFile(crypto: crypto, address: address, version: 3) @@ -75,7 +75,6 @@ class KeystoreUtil: KeystoreUtilProtocol { } static func decode(data: Data, password: String) throws -> Data { - // decode json string guard let keystore = try? JSONDecoder().decode(KeystoreFile.self, from: data) else { throw KeystoreUtilError.decodeFailed @@ -96,7 +95,7 @@ class KeystoreUtil: KeystoreUtilProtocol { } // verify mac - let macKey = derivedKey.subdata(in: 16..<32) + let macKey = derivedKey.subdata(in: 16 ..< 32) let concat = macKey + ciphertext let mac = concat.web3.keccak256 guard mac.web3.hexString.web3.noHexPrefix == keystore.crypto.mac else { @@ -104,7 +103,7 @@ class KeystoreUtil: KeystoreUtilProtocol { } // decrypt ciphertext with encryption key - let encKey = derivedKey.subdata(in: 0..<16) + let encKey = derivedKey.subdata(in: 0 ..< 16) let iv = keystore.crypto.cipherparams.iv.web3.hexData let decryptor = Aes128Util(key: encKey, iv: iv) let privateKey = decryptor.xcrypt(input: ciphertext) diff --git a/web3swift/src/Utils/PropertyWrappers.swift b/web3swift/src/Utils/PropertyWrappers.swift index 340e0140..d4646ef3 100644 --- a/web3swift/src/Utils/PropertyWrappers.swift +++ b/web3swift/src/Utils/PropertyWrappers.swift @@ -20,7 +20,9 @@ struct DataStr: Codable, Equatable, Hashable { public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let str = try container.decode(String.self) - guard let data = Data(hex: str) else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "Data not in '0x' format") } + guard let data = Data(hex: str) else { + throw DecodingError.dataCorruptedError(in: container, debugDescription: "Data not in '0x' format") + } self.value = data } @@ -33,5 +35,4 @@ struct DataStr: Codable, Equatable, Hashable { get { value } set { value = newValue } } - } diff --git a/web3swift/src/Utils/RLP.swift b/web3swift/src/Utils/RLP.swift index effb7bfc..95ba3299 100644 --- a/web3swift/src/Utils/RLP.swift +++ b/web3swift/src/Utils/RLP.swift @@ -7,7 +7,6 @@ import BigInt import Foundation struct RLP { - static func encode(_ item: Any) -> Data? { switch item { case let int as Int: @@ -47,7 +46,7 @@ struct RLP { static func encodeBigInt(_ bint: BigInt) -> Data? { guard bint >= 0 else { - // TODO implement properly to support negatives if RLP supports.. twos complement reverse? + // TODO: implement properly to support negatives if RLP supports.. twos complement reverse? return nil } return encodeBigUInt(BigUInt(bint)) @@ -57,21 +56,21 @@ struct RLP { let data = buint.serialize() let lastIndex = data.count - 1 - let firstIndex = data.firstIndex(where: {$0 != 0x00}) ?? lastIndex + let firstIndex = data.firstIndex(where: { $0 != 0x00 }) ?? lastIndex if lastIndex == -1 { - return Data( [0x80]) + return Data([0x80]) } - let subdata = data.subdata(in: firstIndex.. Data { - if data.count == 1 && data[0] <= 0x7f { + if data.count == 1, data[0] <= 0x7f { return data // single byte, no header } @@ -87,12 +86,11 @@ struct RLP { return nil } encodedData.append(encoded) - /*if let encoded = encode(el) { - encodedData.append(encoded) - } else if let emptyPlaceholder = encodeString("") { - encodedData.append(emptyPlaceholder) - }*/ - + /* if let encoded = encode(el) { + encodedData.append(encoded) + } else if let emptyPlaceholder = encodeString("") { + encodedData.append(emptyPlaceholder) + } */ } var encoded = encodeHeader(size: UInt64(encodedData.count), smallTag: 0xc0, largeTag: 0xf7) @@ -117,13 +115,12 @@ struct RLP { var value = i var bytes = withUnsafeBytes(of: &value) { Array($0) } for (index, byte) in bytes.enumerated().reversed() { - if index != 0 && byte == 0x00 { + if index != 0, byte == 0x00 { bytes.remove(at: index) } else { break } } - return Data( bytes.reversed()) + return Data(bytes.reversed()) } - } From 2a91dedd907983308f1900b3927c9a81ab72e5d8 Mon Sep 17 00:00:00 2001 From: Peerasak Unsakon Date: Tue, 6 Dec 2022 23:10:03 +0700 Subject: [PATCH 05/20] [TIDY] Appease SwiftFormat --- .../Statically Typed/ABIRawType+Static.swift | 255 +++++++++--------- 1 file changed, 127 insertions(+), 128 deletions(-) diff --git a/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift b/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift index 26bf4d49..c195608e 100644 --- a/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift +++ b/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift @@ -149,7 +149,7 @@ extension Data: ABIType { // When decoding it's easier to specify a type, instead of type + static size public protocol ABIStaticSizeDataType: ABIType {} - public struct Data1: ABIStaticSizeDataType { +public struct Data1: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(1) } @@ -159,11 +159,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data2: ABIStaticSizeDataType { +} + +public struct Data2: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(2) } @@ -173,11 +173,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data3: ABIStaticSizeDataType { +} + +public struct Data3: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(3) } @@ -187,11 +187,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data4: ABIStaticSizeDataType { +} + +public struct Data4: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(4) } @@ -201,11 +201,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data5: ABIStaticSizeDataType { +} + +public struct Data5: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(5) } @@ -215,11 +215,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data6: ABIStaticSizeDataType { +} + +public struct Data6: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(6) } @@ -229,11 +229,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data7: ABIStaticSizeDataType { +} + +public struct Data7: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(7) } @@ -243,11 +243,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data8: ABIStaticSizeDataType { +} + +public struct Data8: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(8) } @@ -257,11 +257,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data9: ABIStaticSizeDataType { +} + +public struct Data9: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(9) } @@ -271,11 +271,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data10: ABIStaticSizeDataType { +} + +public struct Data10: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(10) } @@ -285,11 +285,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data11: ABIStaticSizeDataType { +} + +public struct Data11: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(11) } @@ -299,11 +299,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data12: ABIStaticSizeDataType { +} + +public struct Data12: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(12) } @@ -313,11 +313,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data13: ABIStaticSizeDataType { +} + +public struct Data13: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(13) } @@ -327,11 +327,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data14: ABIStaticSizeDataType { +} + +public struct Data14: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(14) } @@ -341,11 +341,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data15: ABIStaticSizeDataType { +} + +public struct Data15: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(15) } @@ -355,11 +355,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data16: ABIStaticSizeDataType { +} + +public struct Data16: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(16) } @@ -369,11 +369,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data17: ABIStaticSizeDataType { +} + +public struct Data17: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(17) } @@ -383,11 +383,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data18: ABIStaticSizeDataType { +} + +public struct Data18: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(18) } @@ -397,11 +397,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data19: ABIStaticSizeDataType { +} + +public struct Data19: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(19) } @@ -411,11 +411,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data20: ABIStaticSizeDataType { +} + +public struct Data20: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(20) } @@ -425,11 +425,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data21: ABIStaticSizeDataType { +} + +public struct Data21: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(21) } @@ -439,11 +439,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data22: ABIStaticSizeDataType { +} + +public struct Data22: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(22) } @@ -453,11 +453,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data23: ABIStaticSizeDataType { +} + +public struct Data23: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(23) } @@ -467,11 +467,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data24: ABIStaticSizeDataType { +} + +public struct Data24: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(24) } @@ -481,11 +481,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data25: ABIStaticSizeDataType { +} + +public struct Data25: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(25) } @@ -495,11 +495,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data26: ABIStaticSizeDataType { +} + +public struct Data26: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(26) } @@ -509,11 +509,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data27: ABIStaticSizeDataType { +} + +public struct Data27: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(27) } @@ -523,11 +523,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data28: ABIStaticSizeDataType { +} + +public struct Data28: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(28) } @@ -537,11 +537,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data29: ABIStaticSizeDataType { +} + +public struct Data29: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(29) } @@ -551,11 +551,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data30: ABIStaticSizeDataType { +} + +public struct Data30: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(30) } @@ -565,11 +565,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data31: ABIStaticSizeDataType { +} + +public struct Data31: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(31) } @@ -579,11 +579,11 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - - public struct Data32: ABIStaticSizeDataType { +} + +public struct Data32: ABIStaticSizeDataType { public static var rawType: ABIRawType { .FixedBytes(32) } @@ -593,10 +593,9 @@ public protocol ABIStaticSizeDataType: ABIType {} var rawData: Data public init(data: Data) { - self.rawData = data + self.rawData = data } - } - +} public struct ABIArray: ABIType { let values: [T] From 6772451861f847161c034db230ec5828d7c547ed Mon Sep 17 00:00:00 2001 From: Pranav Singhal Date: Mon, 2 Jan 2023 15:26:01 +0530 Subject: [PATCH 06/20] Minor correction in Readme (#300) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b85ca9ef..065d852d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Create an instance of `EthereumHttpClient` or `EthereumWebSocketClient`. This wi `EthereumHttpClient` ```swift guard let clientUrl = URL(string: "https://an-infura-or-similar-url.com/123") else { return } -let client = EthereumClient(url: clientUrl) +let client = EthereumHttpClient(url: clientUrl) ``` OR From b16a53b473fbf865d6a9d92719ab24f3d566c6e0 Mon Sep 17 00:00:00 2001 From: frohzinn <31421035+frohzinn@users.noreply.github.com> Date: Mon, 2 Jan 2023 09:30:17 -0800 Subject: [PATCH 07/20] Update KeyUtil.swift (#299) Co-authored-by: Miguel Angel Quinones Garcia --- web3swift/src/Utils/KeyUtil.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/web3swift/src/Utils/KeyUtil.swift b/web3swift/src/Utils/KeyUtil.swift index 6b41f126..787b119f 100644 --- a/web3swift/src/Utils/KeyUtil.swift +++ b/web3swift/src/Utils/KeyUtil.swift @@ -7,7 +7,7 @@ import Logging import secp256k1 import Foundation -enum KeyUtilError: Error { +public enum KeyUtilError: Error { case invalidContext case privateKeyInvalid case unknownError @@ -16,16 +16,16 @@ enum KeyUtilError: Error { case badArguments } -class KeyUtil { +public class KeyUtil { private static var logger: Logger { Logger(label: "web3.swift.key-util") } - static func generatePrivateKeyData() -> Data? { + public static func generatePrivateKeyData() -> Data? { Data.randomOfLength(32) } - static func generatePublicKey(from privateKey: Data) throws -> Data { + public static func generatePublicKey(from privateKey: Data) throws -> Data { guard let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)) else { logger.warning("Failed to generate a public key: invalid context.") throw KeyUtilError.invalidContext @@ -62,13 +62,13 @@ class KeyUtil { return publicKey } - static func generateAddress(from publicKey: Data) -> EthereumAddress { + public static func generateAddress(from publicKey: Data) -> EthereumAddress { let hash = publicKey.web3.keccak256 let address = hash.subdata(in: 12 ..< hash.count) return EthereumAddress(address.web3.hexString) } - static func sign(message: Data, with privateKey: Data, hashing: Bool) throws -> Data { + public static func sign(message: Data, with privateKey: Data, hashing: Bool) throws -> Data { guard let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)) else { logger.warning("Failed to sign message: invalid context.") throw KeyUtilError.invalidContext @@ -109,7 +109,7 @@ class KeyUtil { return signature } - static func recoverPublicKey(message: Data, signature: Data) throws -> String { + public static func recoverPublicKey(message: Data, signature: Data) throws -> String { if signature.count != 65 || message.count != 32 { throw KeyUtilError.badArguments } From ac473f2746be524e95e70827d0c6a3189c265c2e Mon Sep 17 00:00:00 2001 From: Miguel Angel Quinones Date: Tue, 3 Jan 2023 10:50:31 +0100 Subject: [PATCH 08/20] [FIX] ABI for ApprovalForAl --- web3swift/src/ERC721/ERC721Events.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web3swift/src/ERC721/ERC721Events.swift b/web3swift/src/ERC721/ERC721Events.swift index cb794612..3f5e2a88 100644 --- a/web3swift/src/ERC721/ERC721Events.swift +++ b/web3swift/src/ERC721/ERC721Events.swift @@ -49,8 +49,8 @@ public enum ERC721Events { public struct ApprovalForAll: ABIEvent { public static let name = "ApprovalForAll" - public static let types: [ABIType.Type] = [EthereumAddress.self, EthereumAddress.self, BigUInt.self] - public static let typesIndexed = [true, true, true] + public static let types: [ABIType.Type] = [EthereumAddress.self, EthereumAddress.self, Bool.self] + public static let typesIndexed = [true, true, false] public let log: EthereumLog public let from: EthereumAddress From ab93534e94046be2b4bcc135892fddd670d8f752 Mon Sep 17 00:00:00 2001 From: Miguel Angel Quinones Date: Tue, 3 Jan 2023 10:51:38 +0100 Subject: [PATCH 09/20] [FIX] ERC165 ABI should be public --- web3swift/src/ERC165/ERC165.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web3swift/src/ERC165/ERC165.swift b/web3swift/src/ERC165/ERC165.swift index d3accd92..d7f03e02 100644 --- a/web3swift/src/ERC165/ERC165.swift +++ b/web3swift/src/ERC165/ERC165.swift @@ -39,14 +39,14 @@ public enum ERC165Functions { "supportsInterface(bytes4)".web3.keccak256.web3.bytes4 } - struct supportsInterface: ABIFunction { + public struct supportsInterface: ABIFunction { public static let name = "supportsInterface" public let gasPrice: BigUInt? public let gasLimit: BigUInt? public var contract: EthereumAddress public let from: EthereumAddress? - let interfaceId: Data + public let interfaceId: Data public init( contract: EthereumAddress, From 0fdde034364901e86b1b57cc0ec50908d2708906 Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:14:25 +0100 Subject: [PATCH 10/20] Fix: minor typo Fix: minor typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 065d852d..2d57ddd1 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ The library provides some types and helpers to make interacting with web3 and Et - `EthereumAddress`: For representation of addresses, including checksum support. - `BigInt` and `BigUInt`: Using [BigInt](https://github.com/attaswift/BigInt) library -- `EthereumBlock`: Represents the block, either number of RPC-specific defintions like 'Earliest' or 'Latest' +- `EthereumBlock`: Represents the block, either number of RPC-specific definitions like 'Earliest' or 'Latest' - `EthereumTransaction`: Wraps a transaction. Encoders and decoders can work with it to generate proper `data` fields. #### Conversion from and to Foundation types From a9183b522e5833fb3895e885fdebc15b22854345 Mon Sep 17 00:00:00 2001 From: Dionysis Karatzas Date: Thu, 5 Jan 2023 18:36:56 +0200 Subject: [PATCH 11/20] [ADD] logs missing websocket subscription --- web3sTests/Client/EthereumClientTests.swift | 40 +++++++++++++- .../src/Client/EthereumClientProtocol.swift | 6 ++ .../src/Client/EthereumWebSocketClient.swift | 39 +++++++++++-- .../Client/Models/EthereumSubscription.swift | 55 ++++++++++++++----- .../WebSocketNetworkProvider.swift | 7 ++- 5 files changed, 124 insertions(+), 23 deletions(-) diff --git a/web3sTests/Client/EthereumClientTests.swift b/web3sTests/Client/EthereumClientTests.swift index 0f86b27b..d4c1b6b8 100644 --- a/web3sTests/Client/EthereumClientTests.swift +++ b/web3sTests/Client/EthereumClientTests.swift @@ -483,7 +483,7 @@ class EthereumWebSocketClientTests: EthereumClientTests { await waitForExpectations(timeout: 5, handler: nil) XCTAssertNotEqual(subscription.id, "") - XCTAssertEqual(subscription.type, .pendingTransactions) + XCTAssertEqual(subscription.type, .newPendingTransactions) } catch { XCTFail("Expected subscription but failed \(error).") } @@ -512,6 +512,31 @@ class EthereumWebSocketClientTests: EthereumClientTests { } } + func testWebSocketLogs() async { + do { + guard let client = client as? EthereumWebSocketClient else { + XCTFail("Expected client to be EthereumWebSocketClient") + return + } + + var expectation: XCTestExpectation? = self.expectation(description: "Logs") + let type = EthereumSubscriptionType.logs(nil) + let subscription = try await client.logs { log in + print(log) + expectation?.fulfill() + expectation = nil + } + + // we need a high timeout as new block might take a while + await waitForExpectations(timeout: 2500, handler: nil) + + XCTAssertNotEqual(subscription.id, "") + XCTAssertEqual(subscription.type, type) + } catch { + XCTFail("Expected subscription but failed \(error).") + } + } + func testWebSocketSubscribe() async { do { guard let client = client as? EthereumWebSocketClient else { @@ -521,7 +546,7 @@ class EthereumWebSocketClientTests: EthereumClientTests { client.delegate = self delegateExpectation = expectation(description: "onNewPendingTransaction delegate call") - var subscription = try await client.subscribe(type: .pendingTransactions) + var subscription = try await client.subscribe(type: .newPendingTransactions) await waitForExpectations(timeout: 10) _ = try await client.unsubscribe(subscription) @@ -529,6 +554,12 @@ class EthereumWebSocketClientTests: EthereumClientTests { subscription = try await client.subscribe(type: .newBlockHeaders) await waitForExpectations(timeout: 2500) _ = try await client.unsubscribe(subscription) + + delegateExpectation = expectation(description: "onLogs delegate call") + let type = EthereumSubscriptionType.logs(nil) + subscription = try await client.subscribe(type: type) + await waitForExpectations(timeout: 2500) + _ = try await client.unsubscribe(subscription) } catch { XCTFail("Expected subscription but failed \(error).") } @@ -561,4 +592,9 @@ extension EthereumWebSocketClientTests: EthereumWebSocketClientDelegate { delegateExpectation?.fulfill() delegateExpectation = nil } + + func onLog(subscription: EthereumSubscription, log: EthereumLog) { + delegateExpectation?.fulfill() + delegateExpectation = nil + } } diff --git a/web3swift/src/Client/EthereumClientProtocol.swift b/web3swift/src/Client/EthereumClientProtocol.swift index 45a6e6e3..a281b030 100644 --- a/web3swift/src/Client/EthereumClientProtocol.swift +++ b/web3swift/src/Client/EthereumClientProtocol.swift @@ -109,6 +109,9 @@ public protocol EthereumClientProtocol: AnyObject { func newBlockHeaders(onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumHeader) -> Void) func newBlockHeaders(onData: @escaping (EthereumHeader) -> Void) async throws -> EthereumSubscription + func logs(logsParams: LogsParams?, onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumLog) -> Void) + func logs(logsParams: LogsParams?, onData: @escaping (EthereumLog) -> Void) async throws -> EthereumSubscription + func syncing(onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumSyncStatus) -> Void) func syncing(onData: @escaping (EthereumSyncStatus) -> Void) async throws -> EthereumSubscription } @@ -116,6 +119,7 @@ public protocol EthereumClientProtocol: AnyObject { public protocol EthereumWebSocketClientDelegate: AnyObject { func onNewPendingTransaction(subscription: EthereumSubscription, txHash: String) func onNewBlockHeader(subscription: EthereumSubscription, header: EthereumHeader) + func onLog(subscription: EthereumSubscription, log: EthereumLog) func onSyncing(subscription: EthereumSubscription, sync: EthereumSyncStatus) func onWebSocketReconnect() } @@ -125,6 +129,8 @@ public protocol EthereumClientProtocol: AnyObject { func onNewBlockHeader(subscription: EthereumSubscription, header: EthereumHeader) {} + func onLog(subscription: EthereumSubscription, log: EthereumLog) {} + func onSyncing(subscription: EthereumSubscription, sync: EthereumSyncStatus) {} func onWebSocketReconnect() {} diff --git a/web3swift/src/Client/EthereumWebSocketClient.swift b/web3swift/src/Client/EthereumWebSocketClient.swift index 84c42ccf..534b9815 100644 --- a/web3swift/src/Client/EthereumWebSocketClient.swift +++ b/web3swift/src/Client/EthereumWebSocketClient.swift @@ -94,7 +94,7 @@ extension EthereumWebSocketClient: EthereumClientWebSocketProtocol { public func subscribe(type: EthereumSubscriptionType) async throws -> EthereumSubscription { do { - let data = try await networkProvider.send(method: "eth_subscribe", params: [type.method, type.params].compactMap { $0 }, receive: String.self) + let data = try await networkProvider.send(method: "eth_subscribe", params: type.params.compactMap { $0 }, receive: String.self) if let resDataString = data as? String { let subscription = EthereumSubscription(type: type, id: resDataString) provider.addSubscription(subscription, callback: { _ in }) @@ -123,9 +123,9 @@ public func pendingTransactions(onData: @escaping (String) -> Void) async throws -> EthereumSubscription { do { - let data = try await networkProvider.send(method: "eth_subscribe", params: [EthereumSubscriptionType.pendingTransactions.method], receive: String.self) + let data = try await networkProvider.send(method: "eth_subscribe", params: EthereumSubscriptionType.newPendingTransactions.params, receive: String.self) if let resDataString = data as? String { - let subscription = EthereumSubscription(type: .pendingTransactions, id: resDataString) + let subscription = EthereumSubscription(type: .newPendingTransactions, id: resDataString) provider.addSubscription(subscription, callback: { object in onData(object as! String) }) @@ -140,7 +140,7 @@ public func newBlockHeaders(onData: @escaping (EthereumHeader) -> Void) async throws -> EthereumSubscription { do { - let data = try await networkProvider.send(method: "eth_subscribe", params: [EthereumSubscriptionType.newBlockHeaders.method], receive: String.self) + let data = try await networkProvider.send(method: "eth_subscribe", params: EthereumSubscriptionType.newBlockHeaders.params, receive: String.self) if let resDataString = data as? String { let subscription = EthereumSubscription(type: .newBlockHeaders, id: resDataString) provider.addSubscription(subscription, callback: { object in @@ -155,9 +155,27 @@ } } + public func logs(logsParams: LogsParams? = nil, onData: @escaping (EthereumLog) -> Void) async throws -> EthereumSubscription { + do { + let type: EthereumSubscriptionType = .logs(logsParams) + let data = try await networkProvider.send(method: "eth_subscribe", params: type.params, receive: String.self) + if let resDataString = data as? String { + let subscription = EthereumSubscription(type: type, id: resDataString) + provider.addSubscription(subscription, callback: { object in + onData(object as! EthereumLog) + }) + return subscription + } else { + throw EthereumClientError.unexpectedReturnValue + } + } catch { + throw failureHandler(error) + } + } + public func syncing(onData: @escaping (EthereumSyncStatus) -> Void) async throws -> EthereumSubscription { do { - let data = try await networkProvider.send(method: "eth_subscribe", params: [EthereumSubscriptionType.syncing.method], receive: String.self) + let data = try await networkProvider.send(method: "eth_subscribe", params: EthereumSubscriptionType.syncing.params, receive: String.self) if let resDataString = data as? String { let subscription = EthereumSubscription(type: .syncing, id: resDataString) provider.addSubscription(subscription, callback: { object in @@ -218,6 +236,17 @@ } } + public func logs(logsParams: LogsParams? = nil, onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumLog) -> Void) { + Task { + do { + let result = try await logs(logsParams: logsParams, onData: onData) + onSubscribe(.success(result)) + } catch { + failureHandler(error, completionHandler: onSubscribe) + } + } + } + public func syncing(onSubscribe: @escaping (Result) -> Void, onData: @escaping (EthereumSyncStatus) -> Void) { Task { do { diff --git a/web3swift/src/Client/Models/EthereumSubscription.swift b/web3swift/src/Client/Models/EthereumSubscription.swift index 64f3dc23..fc3e3efc 100644 --- a/web3swift/src/Client/Models/EthereumSubscription.swift +++ b/web3swift/src/Client/Models/EthereumSubscription.swift @@ -7,31 +7,56 @@ import Foundation public enum EthereumSubscriptionType: Equatable, Hashable { case newBlockHeaders - case pendingTransactions + case logs(LogsParams?) + case newPendingTransactions case syncing - var method: String { + var params: [EthereumSubscriptionParamElement] { switch self { case .newBlockHeaders: - return "newHeads" - case .pendingTransactions: - return "newPendingTransactions" + return [.method("newHeads")] + case let .logs(params): + return [.method("logs"), .logsParams(params ?? .init(address: nil, topics: nil))] + case .newPendingTransactions: + return [.method("newPendingTransactions")] case .syncing: - return "syncing" + return [.method("syncing")] } } - - struct LogParams: Encodable { - let address: [EthereumAddress] - let topics: [String?] - } - - var params: String? { - nil - } } public struct EthereumSubscription: Hashable { let type: EthereumSubscriptionType let id: String } + +public enum EthereumSubscriptionParamElement: Encodable { + case method(String) + case logsParams(LogsParams) + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case let .method(x): + try container.encode(x) + case let .logsParams(x): + try container.encode(x) + } + } +} + +// MARK: - ParamClass +public struct LogsParams: Codable, Equatable, Hashable { + public let address: EthereumAddress? + public let topics: [String]? + + enum CodingKeys: String, CodingKey { + case address = "address" + case topics = "topics" + } + + public init(address: EthereumAddress?, topics: [String]?) { + self.address = address + self.topics = topics + } +} diff --git a/web3swift/src/Client/NetworkProviders/WebSocketNetworkProvider.swift b/web3swift/src/Client/NetworkProviders/WebSocketNetworkProvider.swift index f4970a5c..d04634b5 100644 --- a/web3swift/src/Client/NetworkProviders/WebSocketNetworkProvider.swift +++ b/web3swift/src/Client/NetworkProviders/WebSocketNetworkProvider.swift @@ -331,7 +331,7 @@ self.delegate?.onNewBlockHeader(subscription: subscription.key, header: response.params.result) subscription.value(response.params.result) } - case .pendingTransactions: + case .newPendingTransactions: if let data = string.data(using: .utf8), let response = try? JSONDecoder().decode(JSONRPCSubscriptionResponse.self, from: data) { self.delegate?.onNewPendingTransaction(subscription: subscription.key, txHash: response.params.result) subscription.value(response.params.result) @@ -341,6 +341,11 @@ self.delegate?.onSyncing(subscription: subscription.key, sync: response.params.result) subscription.value(response.params.result) } + case .logs: + if let data = string.data(using: .utf8), let response = try? JSONDecoder().decode(JSONRPCSubscriptionResponse.self, from: data) { + self.delegate?.onLog(subscription: subscription.key, log: response.params.result) + subscription.value(response.params.result) + } } } From 4de27257b915dd1937e4a10c1314b255d3f0f723 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:27:02 +0000 Subject: [PATCH 12/20] Bump activesupport from 6.1.5 to 6.1.7.2 Bumps [activesupport](https://github.com/rails/rails) from 6.1.5 to 6.1.7.2. - [Release notes](https://github.com/rails/rails/releases) - [Changelog](https://github.com/rails/rails/blob/v7.0.4.2/activesupport/CHANGELOG.md) - [Commits](https://github.com/rails/rails/compare/v6.1.5...v6.1.7.2) --- updated-dependencies: - dependency-name: activesupport dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3a4f3516..792721a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ GEM specs: CFPropertyList (3.0.5) rexml - activesupport (6.1.5) + activesupport (6.1.7.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -54,7 +54,7 @@ GEM netrc (~> 0.11) cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.1.10) + concurrent-ruby (1.2.0) escape (0.0.4) ethon (0.15.0) ffi (>= 1.15.0) @@ -63,10 +63,10 @@ GEM fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.8.3) - i18n (1.10.0) + i18n (1.12.0) concurrent-ruby (~> 1.0) json (2.6.1) - minitest (5.15.0) + minitest (5.17.0) molinillo (0.8.0) nanaimo (0.3.0) nap (1.1.0) @@ -76,7 +76,7 @@ GEM ruby-macho (2.5.1) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (2.0.4) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) xcodeproj (1.21.0) CFPropertyList (>= 2.3.3, < 4.0) @@ -85,7 +85,7 @@ GEM colored2 (~> 3.1) nanaimo (~> 0.3.0) rexml (~> 3.2.4) - zeitwerk (2.5.4) + zeitwerk (2.6.6) PLATFORMS ruby From 921e4362b9ea56bc556e4d01e10b4a66325609f2 Mon Sep 17 00:00:00 2001 From: Miguel Angel Quinones Date: Tue, 14 Feb 2023 12:20:40 +0100 Subject: [PATCH 13/20] [FIX] ENS target addresses have changed --- web3sTests/ENS/ENSOffchainTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web3sTests/ENS/ENSOffchainTests.swift b/web3sTests/ENS/ENSOffchainTests.swift index 0f1e1ada..65493ae6 100644 --- a/web3sTests/ENS/ENSOffchainTests.swift +++ b/web3sTests/ENS/ENSOffchainTests.swift @@ -60,7 +60,7 @@ class ENSOffchainTests: XCTestCase { ens: "resolver.eth", mode: .allowOffchainLookup ) - XCTAssertEqual(EthereumAddress("0xe264d5bb84ba3b8061adc38d3d76e6674ab91852"), ens) + XCTAssertEqual(EthereumAddress("0x19c2d5d0f035563344dbb7be5fd09c8dad62b001"), ens) } catch { XCTFail("Expected ens but failed \(error).") } @@ -104,7 +104,7 @@ class ENSOffchainTests: XCTestCase { ens: "resolver.eth", mode: .allowOffchainLookup ) - XCTAssertEqual(EthereumAddress("0xe264d5bb84ba3b8061adc38d3d76e6674ab91852"), ens) + XCTAssertEqual(EthereumAddress("0x19c2d5d0f035563344dbb7be5fd09c8dad62b001"), ens) } catch { XCTFail("Expected ens but failed \(error).") } @@ -139,7 +139,7 @@ class ENSOffchainTests: XCTestCase { ens: "resolver.eth", mode: .allowOffchainLookup ) - XCTAssertEqual(EthereumAddress("0xe264d5bb84ba3b8061adc38d3d76e6674ab91852"), ens) + XCTAssertEqual(EthereumAddress("0x19c2d5d0f035563344dbb7be5fd09c8dad62b001"), ens) } catch { XCTFail("Expected ens but failed \(error).") } From 036a8a0bbe43b3f540ed062f4a8b0f0095d6db43 Mon Sep 17 00:00:00 2001 From: Miguel Angel Quinones Date: Tue, 14 Feb 2023 14:48:57 +0100 Subject: [PATCH 14/20] [CHANGE] Remove test key from repository --- .github/workflows/CI.yml | 8 ++++++++ .gitignore | 1 + README.md | 4 +++- scripts/setupKey.sh | 4 ++++ web3sTests/TestConfig.swift | 6 +++--- 5 files changed, 19 insertions(+), 4 deletions(-) create mode 100755 scripts/setupKey.sh diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fdd34813..2adf724b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -14,8 +14,12 @@ jobs: macos: needs: lint runs-on: macos-latest + env: + TESTS_PRIVATEKEY: ${{ secrets.TESTS_PRIVATEKEY }} steps: - uses: actions/checkout@v3 + - name: SetupKey + run: ./scripts/setupKey.sh "${{ secrets.TESTS_PRIVATEKEY }}" - name: Build run: swift build -v - name: Tests @@ -25,8 +29,12 @@ jobs: runs-on: ubuntu-latest container: image: swift:5.5-bionic + env: + TESTS_PRIVATEKEY: ${{ secrets.TESTS_PRIVATEKEY }} steps: - uses: actions/checkout@v3 + - name: SetupKey + run: ./scripts/setupKey.sh "${{ secrets.TESTS_PRIVATEKEY }}" - name: Build run: swift build -v - name: Tests diff --git a/.gitignore b/.gitignore index 38dc9aff..08235dd3 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,4 @@ fastlane/screenshots fastlane/test_output scripts/bin/* +TestConfig_private.swift diff --git a/README.md b/README.md index 2d57ddd1..ca8f4bd4 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,9 @@ We support querying ERC721 token data via the `ERC721` struct. Including: ### Running Tests -The tests will all pass when running against Goerli. You will need to provide a URL for the blockchain proxy (e.g. on Infura), and a key-pair in `TestConfig.swift`. Some of the account signing tests will fail, given the signature assertions are against a specific (unprovided) key. +Some of the tests require a private key, which is not stored in the repository. You can ignore these while testing locally, as CI will use the encrypted secret key from Github. + +It's better to run only the tests you need, instead of the whole test suite while developing. If you ever need to set up the key locally, take a look at `TestConfig.swift` where you can manually set it up. Alternatively you can set it up by calling the script `setupKey.sh` and passing the value (adding 0x) so it's written to an ignored file. ## Dependencies diff --git a/scripts/setupKey.sh b/scripts/setupKey.sh new file mode 100755 index 00000000..68e38b70 --- /dev/null +++ b/scripts/setupKey.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +# write first argument to a Test +echo "extension TestConfig {\nstatic let privateKey = \"$1\"\n}" > web3sTests/TestConfig_private.swift \ No newline at end of file diff --git a/web3sTests/TestConfig.swift b/web3sTests/TestConfig.swift index f81ce600..1a7b7a68 100644 --- a/web3sTests/TestConfig.swift +++ b/web3sTests/TestConfig.swift @@ -13,11 +13,11 @@ struct TestConfig { // This is the proxy wss URL for connecting to the Blockchain. For testing we usually use the Goerli network on Infura. Using free tier, so might hit rate limits static let wssUrl = "wss://goerli.infura.io/ws/v3/b2f4b3f635d8425c96854c3d28ba6bb0" - // An EOA with some Ether, so that we can test sending transactions (pay for gas) - static let privateKey = "0xef4e182ae2cf32192d2a62c1159c8c4f7f2d658c303d0dfca5791a205456a132" + // An EOA with some Ether, so that we can test sending transactions (pay for gas). Set by CI +// static let privateKey = "SET_YOUR_KEY_HERE" // This is the expected public key (address) from the above private key - static let publicKey = "0x719561fee351F7aC6560D0302aE415FfBEEc0B51" + static let publicKey = "0xE78e5ecb061fE3DD1672dDDA7b5116213B23B99A" // A test ERC20 token contract (UNI) static let erc20Contract = "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" From 3b69adbb5454b128fd2a2ed546731241fdf490d8 Mon Sep 17 00:00:00 2001 From: Miguel Angel Quinones Date: Tue, 14 Feb 2023 20:23:13 +0100 Subject: [PATCH 15/20] [TIDY] --- scripts/setupKey.sh | 2 +- web3sTests/TestConfig.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/setupKey.sh b/scripts/setupKey.sh index 68e38b70..49484d92 100755 --- a/scripts/setupKey.sh +++ b/scripts/setupKey.sh @@ -1,4 +1,4 @@ #!/bin/sh # write first argument to a Test -echo "extension TestConfig {\nstatic let privateKey = \"$1\"\n}" > web3sTests/TestConfig_private.swift \ No newline at end of file +echo "extension TestConfig {\nstatic let privateKey = \"$1\"\nstatic let publicKey = \"0xE78e5ecb061fE3DD1672dDDA7b5116213B23B99A\"\n}" > web3sTests/TestConfig_private.swift \ No newline at end of file diff --git a/web3sTests/TestConfig.swift b/web3sTests/TestConfig.swift index 1a7b7a68..b3571891 100644 --- a/web3sTests/TestConfig.swift +++ b/web3sTests/TestConfig.swift @@ -17,7 +17,7 @@ struct TestConfig { // static let privateKey = "SET_YOUR_KEY_HERE" // This is the expected public key (address) from the above private key - static let publicKey = "0xE78e5ecb061fE3DD1672dDDA7b5116213B23B99A" +// static let publicKey = "SET_YOUR_PUBLIC_ADDRESS_HERE" // A test ERC20 token contract (UNI) static let erc20Contract = "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" From 10b65fba44b08d3b3d96d14dfdd13889d38fa12b Mon Sep 17 00:00:00 2001 From: Miguel Angel Quinones Garcia Date: Wed, 15 Feb 2023 10:56:27 +0100 Subject: [PATCH 16/20] Update CI.yml --- .github/workflows/CI.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2adf724b..31c07d23 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -3,6 +3,7 @@ name: CI on: pull_request: branches: [ develop ] + workflow_dispatch: jobs: lint: From 040ad81e2928780117db3090f9904f50aeca4cec Mon Sep 17 00:00:00 2001 From: Miguel Angel Quinones Garcia Date: Wed, 15 Feb 2023 14:37:46 +0100 Subject: [PATCH 17/20] Use pull-request-target in CI runs See https://docs.github.com/en/actions/managing-workflow-runs/approving-workflow-runs-from-public-forks and https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/ --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 31c07d23..8ac1c0a7 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,7 +1,7 @@ name: CI on: - pull_request: + pull_request_target: branches: [ develop ] workflow_dispatch: From cf700b79c7e9a4c45f38404d213e8726002f0897 Mon Sep 17 00:00:00 2001 From: Tung Nguyen Date: Wed, 15 Feb 2023 21:34:20 +0700 Subject: [PATCH 18/20] [ADD] Support multicall v2 (#308) --- web3sTests/Multicall/MulticallTests.swift | 31 ++++++++++ web3swift/src/Multicall/Multicall.swift | 60 +++++++++++++++++++ .../src/Multicall/MulticallContract.swift | 32 ++++++++++ 3 files changed, 123 insertions(+) diff --git a/web3sTests/Multicall/MulticallTests.swift b/web3sTests/Multicall/MulticallTests.swift index 938a6d43..342b2774 100644 --- a/web3sTests/Multicall/MulticallTests.swift +++ b/web3sTests/Multicall/MulticallTests.swift @@ -47,6 +47,37 @@ class MulticallTests: XCTestCase { XCTAssertEqual(decimals, 18) XCTAssertEqual(name, "Uniswap") } + + func testNameAndSymbolMulticall2() async throws { + var aggregator = Multicall.Aggregator() + + var name: String? + var decimals: UInt8? + + try aggregator.append(ERC20Functions.decimals(contract: testContractAddress)) { output in + decimals = try ERC20Responses.decimalsResponse(data: output.get())?.value + } + + try aggregator.append( + function: ERC20Functions.name(contract: testContractAddress), + response: ERC20Responses.nameResponse.self + ) { result in + name = try? result.get() + } + + try aggregator.append(ERC20Functions.symbol(contract: testContractAddress)) + + do { + let response = try await multicall.tryAggregate(requireSuccess: true, calls: aggregator.calls) + let symbol = try ERC20Responses.symbolResponse(data: try response.outputs[2].get())?.value + XCTAssertEqual(symbol, "UNI") + } catch { + XCTFail("Unexpected failure while handling output") + } + + XCTAssertEqual(decimals, 18) + XCTAssertEqual(name, "Uniswap") + } } class MulticallWebSocketTests: MulticallTests { diff --git a/web3swift/src/Multicall/Multicall.swift b/web3swift/src/Multicall/Multicall.swift index 4b62546e..b308d9e2 100644 --- a/web3swift/src/Multicall/Multicall.swift +++ b/web3swift/src/Multicall/Multicall.swift @@ -37,6 +37,21 @@ public struct Multicall { throw MulticallError.executionFailed(error) } } + + public func tryAggregate(requireSuccess: Bool, calls: [Call]) async throws -> Multicall.Multicall2Response { + let function = Contract.Functions.tryAggregate(contract: Contract.multicall2Address, requireSuccess: requireSuccess, calls: calls) + + do { + let data = try await function.call(withClient: client, responseType: Multicall2Response.self) + zip(calls, data.outputs) + .forEach { call, output in + try? call.handler?(output) + } + return data + } catch { + throw MulticallError.executionFailed(error) + } + } } extension Multicall { @@ -50,6 +65,17 @@ extension Multicall { } } } + + public func tryAggregate(requireSuccess: Bool, calls: [Call], completionHandler: @escaping (Result) -> Void) { + Task { + do { + let res = try await tryAggregate(requireSuccess: requireSuccess, calls: calls) + completionHandler(.success(res)) + } catch let error as MulticallError { + completionHandler(.failure(error)) + } + } + } } extension Multicall { @@ -85,6 +111,40 @@ extension Multicall { } } + public struct Multicall2Result: ABITuple { + public static var types: [ABIType.Type] = [Bool.self, String.self] + public var encodableValues: [ABIType] { [success, returnData] } + + public let success: Bool + public let returnData: String + + public init?(values: [ABIDecoder.DecodedValue]) throws { + self.success = try values[0].decoded() + self.returnData = try values[1].entry[0] + } + + public func encode(to encoder: ABIFunctionEncoder) throws { + try encoder.encode(success) + try encoder.encode(returnData) + } + } + + public struct Multicall2Response: ABIResponse { + static let multicallFailedError = "MULTICALL_FAIL".web3.keccak256.web3.hexString + public static var types: [ABIType.Type] = [ABIArray.self] + public let outputs: [Output] + + public init?(values: [ABIDecoder.DecodedValue]) throws { + let results: [Multicall2Result] = try values[0].decodedTupleArray() + self.outputs = results.map { result in + guard result.returnData != Self.multicallFailedError else { + return .failure(.contractFailure) + } + return .success(result.returnData) + } + } + } + public struct Call: ABITuple { public static var types: [ABIType.Type] = [EthereumAddress.self, Data.self] public var encodableValues: [ABIType] { [target, encodedFunction] } diff --git a/web3swift/src/Multicall/MulticallContract.swift b/web3swift/src/Multicall/MulticallContract.swift index b740cf0e..7a06a94b 100644 --- a/web3swift/src/Multicall/MulticallContract.swift +++ b/web3swift/src/Multicall/MulticallContract.swift @@ -10,6 +10,7 @@ extension Multicall { public enum Contract { static let goerliAddress: EthereumAddress = "0x77dCa2C955b15e9dE4dbBCf1246B4B85b651e50e" static let mainnetAddress: EthereumAddress = "0xF34D2Cb31175a51B23fb6e08cA06d7208FaD379F" + static let multicall2Address: EthereumAddress = "0x5ba1e12693dc8f9c48aad8770482f4739beed696" public static func registryAddress(for network: EthereumNetwork) -> EthereumAddress? { switch network { @@ -49,6 +50,37 @@ extension Multicall { try encoder.encode(calls) } } + + public struct tryAggregate: ABIFunction { + public static let name = "tryAggregate" + public let gasPrice: BigUInt? + public let gasLimit: BigUInt? + public var contract: EthereumAddress + public let from: EthereumAddress? + public let requireSuccess: Bool + public let calls: [Call] + + public init( + contract: EthereumAddress, + from: EthereumAddress? = nil, + gasPrice: BigUInt? = nil, + gasLimit: BigUInt? = nil, + requireSuccess: Bool, + calls: [Call] + ) { + self.contract = contract + self.gasPrice = gasPrice + self.gasLimit = gasLimit + self.from = from + self.requireSuccess = requireSuccess + self.calls = calls + } + + public func encode(to encoder: ABIFunctionEncoder) throws { + try encoder.encode(requireSuccess) + try encoder.encode(calls) + } + } } } } From cf217ea95fcaf29c69db7b8b2a4c2ebd377c162d Mon Sep 17 00:00:00 2001 From: Miguel Angel Quinones Garcia Date: Wed, 15 Feb 2023 21:27:46 +0100 Subject: [PATCH 19/20] [UPDATE] Version --- web3.swift.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web3.swift.podspec b/web3.swift.podspec index d247d974..3a27d3f7 100644 --- a/web3.swift.podspec +++ b/web3.swift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'web3.swift' - s.version = '1.4.1' + s.version = '1.5.0' s.license = 'MIT' s.summary = 'Ethereum API for Swift' s.homepage = 'https://github.com/argentlabs/web3.swift' From 1ad8cc4b6e97a4053d8c3f070063e7c6c62a26ba Mon Sep 17 00:00:00 2001 From: Miguel Angel Quinones Garcia Date: Thu, 16 Feb 2023 09:45:41 +0100 Subject: [PATCH 20/20] Update README.md --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ca8f4bd4..05f5c662 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,11 @@ If using `async/await` you can `await` on the result let txHash = try await client.eth_sendRawTransaction(transaction, withAccount: account) ``` +## Generating ABI from a smart contract ABI file +Currently we don't support code generation as making it properly is a bigger project, and should possibly live outside of this repository. + +You can try this project instead: [imanrep/swiftabigen](https://github.com/imanrep/swiftabigen) + ### Data types The library provides some types and helpers to make interacting with web3 and Ethereum easier. @@ -171,17 +176,14 @@ We built web3.swift to be as lightweight as possible. However, given the cryptog - [Tiny AES](https://github.com/kokke/tiny-AES-c): A small and portable implementation of the AES ECB, CTR and CBC encryption algorithms. - [secp256k1.swift](https://github.com/Boilertalk/secp256k1.swift) -We also use Apple's own CommonCrypto (via [this](https://github.com/sergejp/CommonCrypto) method) and [BigInt](https://github.com/attaswift/BigInt) via CocoaPod dependency. - -## Todos - -There are some features that have yet to be fully implemented! Not every RPC method is currently supported, and here's some other suggestions we would like to see in the future: - +Package dependencies: +- [BigInt](https://github.com/attaswift/BigInt) +- [GenericJSON](https://github.com/iwill/generic-json-swift) +- [secp256k1](https://github.com/GigaBitcoin/secp256k1.swift.git) +- [Vapor Websocket](https://github.com/vapor/websocket-kit.git) +- [Apple Swift-log](https://github.com/apple/swift-log.git) -- Batch support for JSONRPC interface -- Use a Hex struct for values to be more explicit in expected types -- Use [Truffle](https://github.com/trufflesuite/ganache-cli) for running tests -- Bloom Filter support +Also for Linux build, we can't se Apple crypto APIs, so we embedded a small subset of CryptoSwift (instead of importing the whole library). Credit to [Marcin Krzyżanowski](https://github.com/krzyzanowskim/CryptoSwift) ## Contributors