diff --git a/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter+Encodable.swift b/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter+Encodable.swift index a683d5fdc..835048fbf 100644 --- a/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter+Encodable.swift +++ b/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter+Encodable.swift @@ -8,23 +8,23 @@ import Foundation extension RequestParameter: Encodable { - /** - This encoder encodes `RequestParameter` associated value ignoring self value - - This is required to encode mixed types array, like - - ```swift - let someArray: [RequestParameter] = [ - .init(rawValue: 12)!, - .init(rawValue: "this")!, - .init(rawValue: 12.2)!, - .init(rawValue: [12.2, 12.4])! - ] - let encoded = try JSONEncoder().encode(someArray) - print(String(data: encoded, encoding: .utf8)!) - //> [12,\"this\",12.2,[12.2,12.4]]` - ``` - */ + + /// This encoder encodes `RequestParameter` associated value ignoring self value + /// + /// This is required to encode mixed types array, like + /// + /// ```swift + /// let someArray: [RequestParameter] = [ + /// .init(rawValue: 12)!, + /// .init(rawValue: "this")!, + /// .init(rawValue: 12.2)!, + /// .init(rawValue: [12.2, 12.4])! + /// ] + /// let encoded = try JSONEncoder().encode(someArray) + /// print(String(data: encoded, encoding: .utf8)!) + /// //> [12,\"this\",12.2,[12.2,12.4]]` + /// ``` + /// - Parameter encoder: The encoder to write data to. func encode(to encoder: Encoder) throws { var enumContainer = encoder.singleValueContainer() /// force casting in this switch is safe because diff --git a/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter+RawRepresentable.swift b/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter+RawRepresentable.swift index 5cdad289f..ef04032ba 100644 --- a/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter+RawRepresentable.swift +++ b/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter+RawRepresentable.swift @@ -8,14 +8,15 @@ import Foundation extension RequestParameter: RawRepresentable { - /** - This init is required by ``RawRepresentable`` protocol, which is required to encode mixed type values array in JSON. - This protocol is used to implement custom `encode` method for that enum, - which encodes an array of self-assosiated values. - - You're totally free to use explicit and more convenience member init as `RequestParameter.int(12)` in your code. - */ + /// This init is required by ``RawRepresentable`` protocol, which is required + /// to encode mixed type values array in JSON. + /// + /// This protocol is used to implement custom `encode` method for that enum, + /// which encodes an array of self-assosiated values. + /// + /// You're totally free to use explicit and more convenience member init as `RequestParameter.int(12)` in your code. + /// - Parameter rawValue: one of the supported types like `Int`, `UInt` etc. init?(rawValue: APIRequestParameterType) { /// force casting in this switch is safe because /// each `rawValue` forced to casts only in exact case which is runs based on `rawValues` type diff --git a/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter.swift b/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter.swift index 65c3059fa..ff0d526a6 100644 --- a/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter.swift +++ b/Sources/Web3Core/EthereumNetwork/RequestParameter/RequestParameter.swift @@ -1,39 +1,41 @@ // // RequestParameter.swift -// +// // // Created by Yaroslav Yashin on 12.07.2022. // import Foundation -/** - Enum to compose request to the node params. - - In most cases request params are passed to Ethereum JSON RPC request as array of mixed type values, such as `[12,"this",true]`. - - Meanwhile swift don't provide strict way to compose such array it gives some hacks to solve this task - and one of them is using `RawRepresentable` protocol. - - This protocol allows the designated type to represent itself in `String` representation. - - So in our case we're using it to implement custom `encode` method for all possible types used as request params. - - This `encode` method is required to encode array of `RequestParameter` to not to `[RequestParameter.int(1)]`, but to `[1]`. - - Here's an example of using this enum in field. - ```swift - let jsonRPCParams: [APIRequestParameterType] = [ - .init(rawValue: 12)!, - .init(rawValue: "this")!, - .init(rawValue: 12.2)!, - .init(rawValue: [12.2, 12.4])! - ] - let encoded = try JSONEncoder().encode(jsonRPCParams) - print(String(data: encoded, encoding: .utf8)!) - //> [12,\"this\",12.2,[12.2,12.4]]` - ``` - */ +/// Enum to compose request to the node params. +/// +/// In most cases request params are passed to Ethereum JSON RPC request as array of +/// mixed type values, such as `[12,"this",true]`. +/// +/// Meanwhile swift don't provide strict way to compose such array it gives +/// some hacks to solve this task +/// and one of them is using `RawRepresentable` protocol. +/// +/// This protocol allows the designated type to represent itself in `String` representation. +/// +/// So in our case we're using it to implement custom `encode` method for all possible +/// types used as request params. +/// +/// This `encode` method is required to encode array of `RequestParameter` +/// to not to `[RequestParameter.int(1)]`, but to `[1]`. +/// +/// Here's an example of using this enum in field. +/// ```swift +/// let jsonRPCParams: [APIRequestParameterType] = [ +/// .init(rawValue: 12)!, +/// .init(rawValue: "this")!, +/// .init(rawValue: 12.2)!, +/// .init(rawValue: [12.2, 12.4])! +/// ] +/// let encoded = try JSONEncoder().encode(jsonRPCParams) +/// print(String(data: encoded, encoding: .utf8)!) +/// //> [12,\"this\",12.2,[12.2,12.4]]` +/// ``` enum RequestParameter { case int(Int) case intArray([Int]) diff --git a/Sources/Web3Core/KeystoreManager/BIP32HDNode.swift b/Sources/Web3Core/KeystoreManager/BIP32HDNode.swift index db7c868fb..55a366f79 100755 --- a/Sources/Web3Core/KeystoreManager/BIP32HDNode.swift +++ b/Sources/Web3Core/KeystoreManager/BIP32HDNode.swift @@ -97,7 +97,7 @@ public class HDNode { let hmacKey = "Bitcoin seed".data(using: .ascii)! let hmac: Authenticator = HMAC(key: hmacKey.bytes, variant: HMAC.Variant.sha2(.sha512)) guard let entropy = try? hmac.authenticate(seed.bytes) else { return nil } - guard entropy.count == 64 else { return nil} + guard entropy.count == 64 else { return nil } let I_L = entropy[0..<32] let I_R = entropy[32..<64] chaincode = Data(I_R) diff --git a/Sources/Web3Core/KeystoreManager/BIP44.swift b/Sources/Web3Core/KeystoreManager/BIP44.swift index 9a80a396c..d093115fc 100644 --- a/Sources/Web3Core/KeystoreManager/BIP44.swift +++ b/Sources/Web3Core/KeystoreManager/BIP44.swift @@ -6,19 +6,18 @@ import Foundation public protocol BIP44 { - /** - Derive an ``HDNode`` based on the provided path. The function will throw ``BIP44Error.warning`` if it was invoked with `throwOnWarning` equal to - `true` and the root key doesn't have a previous child with at least one transaction. If it is invoked with `throwOnWarning` equal to `false` the child node will be - derived directly using the derive function of ``HDNode``. This function needs to query the blockchain history when `throwOnWarning` is `true`, so it can throw - network errors. - - Parameter path: valid BIP44 path. - - Parameter throwOnWarning: `true` to use - [Account Discovery](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account-discovery) standard, - otherwise it will dervive the key using the derive function of ``HDNode``. - - Throws: ``BIP44Error.warning`` if the child key shouldn't be used according to - [Account Discovery](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account-discovery) standard. - - Returns: an ``HDNode`` child key for the provided `path` if it can be created, otherwise `nil` - */ + /// Derive an ``HDNode`` based on the provided path. The function will throw ``BIP44Error.warning`` + /// if it was invoked with `throwOnWarning` equal to `true` and the root key doesn't have a previous child + /// with at least one transaction. If it is invoked with `throwOnWarning` equal to `false` the child node will + /// be derived directly using the derive function of ``HDNode``. This function needs to query the blockchain + /// history when `throwOnWarning` is `true`, so it can throw network errors. + /// - Parameter path: valid BIP44 path. + /// - Parameter throwOnWarning: `true` to use + /// [Account Discovery](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account-discovery) standard, + /// otherwise it will dervive the key using the derive function of ``HDNode``. + /// - Throws: ``BIP44Error.warning`` if the child key shouldn't be used according to + /// [Account Discovery](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account-discovery) standard. + /// - Returns: an ``HDNode`` child key for the provided `path` if it can be created, otherwise `nil` func derive(path: String, throwOnWarning: Bool, transactionChecker: TransactionChecker) async throws -> HDNode? } @@ -35,12 +34,10 @@ public enum BIP44Error: LocalizedError, Equatable { } public protocol TransactionChecker { - /** - It verifies if the provided address has at least one transaction - - Parameter address: to be queried - - Throws: any error related to query the blockchain provider - - Returns: `true` if the address has at least one transaction, `false` otherwise - */ + /// It verifies if the provided address has at least one transaction + /// - Parameter ethereumAddress: to be queried + /// - Throws: any error related to query the blockchain provider + /// - Returns: `true` if the address has at least one transaction, `false` otherwise func hasTransactions(ethereumAddress: EthereumAddress) async throws -> Bool } @@ -104,12 +101,11 @@ extension String { return account } - /** - Transforms a bip44 path into a new one changing account & index. The resulting one will have the change value equal to `0` to represent the external chain. The format will be `m/44'/coin_type'/account'/change/address_index` - - Parameter account: the new account to use - - Parameter addressIndex: the new addressIndex to use - - Returns: a valid bip44 path with the provided account, addressIndex and external change or `nil` otherwise - */ + /// Transforms a bip44 path into a new one changing account & index. The resulting one will have the change value equal to `0` to + /// represent the external chain. The format will be `m/44'/coin_type'/account'/change/address_index` + /// - Parameter account: the new account to use + /// - Parameter addressIndex: the new addressIndex to use + /// - Returns: a valid bip44 path with the provided account, addressIndex and external change or `nil` otherwise func newPath(account: Int, addressIndex: Int) -> String? { guard isBip44Path else { return nil diff --git a/Sources/Web3Core/Transaction/EventfilterParameters.swift b/Sources/Web3Core/Transaction/EventfilterParameters.swift index 7459f2dda..9850feb72 100755 --- a/Sources/Web3Core/Transaction/EventfilterParameters.swift +++ b/Sources/Web3Core/Transaction/EventfilterParameters.swift @@ -56,44 +56,42 @@ extension EventFilterParameters { } extension EventFilterParameters { - /** - This enum covers the optional nested Arrays - - ``EventFilterParameters`` include ``topic`` property with is array of optional values, - and where `nil` value is a thing, and should be kept in server request. - - This is not a trivial case for swift lang or any other stricktly typed lang. - - So to make this possible ``Topic`` enum is provided. - - It handle two cases: ``.string(String?)`` and ``.strings([Topic?]?)``, - where former should be used to assign first demention value, - and the latter to assign second dimension value into ``EventFilterParameters.topics`` property. - - So to encode as a parameter follow JSON array: - ```JSON - [ - "0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", - null, - [ - "0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc" - ] - ] - ``` - - you have to pass to the ``topics`` property follow swift array: - ```swift - let topics: [Topic?] = [ - .string("0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"), - .string(nil), - .strings([ - .string("0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"), - .string("0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"), - ]) - ] - ``` - */ + /// This enum covers the optional nested Arrays + /// + /// ``EventFilterParameters`` include ``topic`` property with is array of optional values, + /// and where `nil` value is a thing, and should be kept in server request. + /// + /// This is not a trivial case for swift lang or any other stricktly typed lang. + /// + /// So to make this possible ``Topic`` enum is provided. + /// + /// It handle two cases: ``.string(String?)`` and ``.strings([Topic?]?)``, + /// where former should be used to assign first demention value, + /// and the latter to assign second dimension value into ``EventFilterParameters.topics`` property. + /// + /// So to encode as a parameter follow JSON array: + /// ```JSON + /// [ + /// "0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", + /// null, + /// [ + /// "0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", + /// "0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc" + /// ] + /// ] + /// ``` + /// + /// you have to pass to the ``topics`` property follow swift array: + /// ```swift + /// let topics: [Topic?] = [ + /// .string("0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"), + /// .string(nil), + /// .strings([ + /// .string("0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"), + /// .string("0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"), + /// ]) + /// ] + /// ``` public enum Topic: Encodable { case string(String?) case strings([Topic?]?) diff --git a/Sources/Web3Core/Utility/Utilities.swift b/Sources/Web3Core/Utility/Utilities.swift index 4524bcd98..a626c3ef6 100644 --- a/Sources/Web3Core/Utility/Utilities.swift +++ b/Sources/Web3Core/Utility/Utilities.swift @@ -202,11 +202,9 @@ public struct Utilities { } /// Recover the Ethereum address from recoverable secp256k1 signature. Message is first hashed using the "personal hash" protocol. - /// BE WARNED - changing a message will result in different Ethereum address, but not in error. - /// - /// Input parameters should be Data objects. + /// BE WARNED - changing a message will result in different Ethereum address, but not in an error. public static func personalECRecover(_ personalMessage: Data, signature: Data) -> EthereumAddress? { - if signature.count != 65 { return nil} + if signature.count != 65 { return nil } let rData = signature[0..<32].bytes let sData = signature[32..<64].bytes var vData = signature[64] @@ -226,10 +224,8 @@ public struct Utilities { /// Recover the Ethereum address from recoverable secp256k1 signature. /// Takes a hash of some message. What message is hashed should be checked by user separately. - /// - /// Input parameters should be Data objects. public static func hashECRecover(hash: Data, signature: Data) -> EthereumAddress? { - if signature.count != 65 { return nil} + if signature.count != 65 { return nil } let rData = signature[0..<32].bytes let sData = signature[32..<64].bytes var vData = signature[64] diff --git a/Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift b/Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift index 88e9d52d0..c903a6af4 100755 --- a/Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift +++ b/Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift @@ -52,7 +52,7 @@ extension Web3.BrowserFunctions { } public func personalECRecover(_ personalMessage: Data, signature: Data) -> String? { - if signature.count != 65 { return nil} + if signature.count != 65 { return nil } let rData = signature[0..<32].bytes let sData = signature[32..<64].bytes var vData = signature[64] diff --git a/Sources/web3swift/Web3/Web3+Personal.swift b/Sources/web3swift/Web3/Web3+Personal.swift index 6a7882849..42e21f6f6 100755 --- a/Sources/web3swift/Web3/Web3+Personal.swift +++ b/Sources/web3swift/Web3/Web3+Personal.swift @@ -9,77 +9,64 @@ import Web3Core extension Web3.Personal { - /** - *Locally or remotely sign a message (arbitrary data) with the private key. To avoid potential signing of a transaction the message is first prepended by a special header and then hashed.* - - - parameters: - - message: Message Data - - from: Use a private key that corresponds to this account - - password: Password for account if signing locally - - - returns: - - Result object - - - important: This call is synchronous - - */ + /// Locally or remotely sign a message (arbitrary data) with the private key. + /// To avoid potential signing of a transaction the message is first prepended by a special header and then hashed. + /// - Parameters: + /// - message: raw message as bytes (e.g. UTF-8 bytes of a string); + /// - from: an account whose private key will be used; + /// - password: password to extract private key; + /// - Returns: signed personal message public func signPersonalMessage(message: Data, from: EthereumAddress, password: String) async throws -> Data { - let result = try await self.signPersonal(message: message, from: from, password: password) + let result = try await signPersonal(message: message, from: from, password: password) return result } - /** - *Unlock an account on the remote node to be able to send transactions and sign messages.* - - - parameters: - - account: EthereumAddress of the account to unlock - - password: Password to use for the account - - seconds: Time interval before automatic account lock by Ethereum node - - - returns: - - Result object - - - important: This call is synchronous. Does nothing if private keys are stored locally. - - */ + /// Unlock an account on the remote node to be able to send transactions and sign messages. + /// - Parameters: + /// - account: EthereumAddress of the account to unlock + /// - password: Password for the account + /// - seconds: Time interval before automatic account lock by Ethereum node. Default 300 + /// - Returns: `true` if account was unlocked. public func unlockAccount(account: EthereumAddress, password: String, seconds: UInt = 300) async throws -> Bool { - let result = try await self.unlock(account: account, password: password) + let result = try await unlock(account: account, password: password) return result } - /** - *Recovers a signer of some message. Message is first prepended by special prefix (check the "signPersonalMessage" method description) and then hashed.* - - - parameters: - - personalMessage: Message Data - - signature: Serialized signature, 65 bytes - - - returns: - - Result object + /// Recovers a signer of some personal message. Message will be first prepended by special prefix + /// (check the "signPersonalMessage" method description) and then hashed before the recovery attempt. + /// + /// If you have a hash instead of a message use ``Web3/Personal/recoverAddress(hash:signature:)`` + /// + /// - Parameters: + /// - message: raw personal message as bytes (e.g. UTF-8 bytes of a string); + /// - signature: signature that is the result of signing the `personalMessage`; + /// - Returns: address of the signer or `nil`. + public func recoverAddress(message: Data, signature: Data) -> EthereumAddress? { + Utilities.personalECRecover(message, signature: signature) + } - */ + @available(*, deprecated, message: "Will be removed in Web3Swift v4. Please, use `func recoverAddress(message: Data, signature: Data) -> EthereumAddress?` instead.") public func ecrecover(personalMessage: Data, signature: Data) throws -> EthereumAddress { - guard let recovered = Utilities.personalECRecover(personalMessage, signature: signature) else { - throw Web3Error.dataError + if let address = recoverAddress(message: personalMessage, signature: signature) { + return address } - return recovered + throw Web3Error.dataError } - /** - *Recovers a signer of some hash. Checking what is under this hash is on behalf of the user.* - - - parameters: - - hash: Signed hash - - signature: Serialized signature, 65 bytes - - - returns: - - Result object + /// Recovers a signer of some hash. + /// - Parameters: + /// - hash: some hash, e.g. hashed personal message; + /// - signature: 65 bytes serialized signature; + /// - Returns: address of the signer or `nil`. + public func recoverAddress(hash: Data, signature: Data) -> EthereumAddress? { + Utilities.hashECRecover(hash: hash, signature: signature) + } - */ + @available(*, deprecated, message: "Will be removed in Web3Swift v4. Please, use `func recoverAddress(hash: Data, signature: Data) -> EthereumAddress?` instead.") public func ecrecover(hash: Data, signature: Data) throws -> EthereumAddress { - guard let recovered = Utilities.hashECRecover(hash: hash, signature: signature) else { - throw Web3Error.dataError + if let address = recoverAddress(hash: hash, signature: signature) { + return address } - return recovered + throw Web3Error.dataError } } diff --git a/Tests/web3swiftTests/localTests/PersonalSignatureTests.swift b/Tests/web3swiftTests/localTests/PersonalSignatureTests.swift index d9027b6e4..e8cd5e1ff 100755 --- a/Tests/web3swiftTests/localTests/PersonalSignatureTests.swift +++ b/Tests/web3swiftTests/localTests/PersonalSignatureTests.swift @@ -22,7 +22,7 @@ class PersonalSignatureTests: XCTestCase { let signature = try await web3.personal.signPersonalMessage(message: message.data(using: .utf8)!, from: expectedAddress, password: "") let unmarshalledSignature = SECP256K1.unmarshalSignature(signatureData: signature)! - let signer = try web3.personal.ecrecover(personalMessage: message.data(using: .utf8)!, signature: signature) + let signer = web3.personal.recoverAddress(message: message.data(using: .utf8)!, signature: signature) XCTAssert(expectedAddress == signer, "Failed to sign personal message") }