diff --git a/Sources/Web3Core/Contract/ContractProtocol.swift b/Sources/Web3Core/Contract/ContractProtocol.swift index b82d8aa8c..5a2ab8863 100755 --- a/Sources/Web3Core/Contract/ContractProtocol.swift +++ b/Sources/Web3Core/Contract/ContractProtocol.swift @@ -143,8 +143,12 @@ public protocol ContractProtocol { /// - name with arguments:`myFunction(uint256)`. /// - method signature (with or without `0x` prefix, case insensitive): `0xFFffFFff`; /// - data: non empty bytes to decode; - /// - Returns: dictionary with decoded values. `nil` if decoding failed. - func decodeReturnData(_ method: String, data: Data) -> [String: Any]? + /// - Returns: dictionary with decoded values. + /// - Throws: + /// - `Web3Error.revert(String, String?)` when function call aborted by `revert(string)` and `require(expression, string)`. + /// - `Web3Error.revertCustom(String, Dictionary)` when function call aborted by `revert CustomError()`. + @discardableResult + func decodeReturnData(_ method: String, data: Data) throws -> [String: Any] /// Decode input arguments of a function. /// - Parameters: @@ -320,13 +324,40 @@ extension DefaultContractProtocol { return bloom.test(topic: event.topic) } - public func decodeReturnData(_ method: String, data: Data) -> [String: Any]? { + @discardableResult + public func decodeReturnData(_ method: String, data: Data) throws -> [String: Any] { if method == "fallback" { - return [String: Any]() + return [:] + } + + guard let function = methods[method]?.first else { + throw Web3Error.inputError(desc: "Make sure ABI you use contains '\(method)' method.") + } + + switch data.count % 32 { + case 0: + return try function.decodeReturnData(data) + case 4: + let selector = data[0..<4] + if selector.toHexString() == "08c379a0", let reason = ABI.Element.EthError.decodeStringError(data[4...]) { + throw Web3Error.revert("revert(string)` or `require(expression, string)` was executed. reason: \(reason)", reason: reason) + } + else if selector.toHexString() == "4e487b71", let reason = ABI.Element.EthError.decodePanicError(data[4...]) { + let panicCode = String(format: "%02X", Int(reason)).addHexPrefix() + throw Web3Error.revert("Error: call revert exception; VM Exception while processing transaction: reverted with panic code \(panicCode)", reason: panicCode) + } + else if let customError = errors[selector.toHexString().addHexPrefix().lowercased()] { + if let errorArgs = customError.decodeEthError(data[4...]) { + throw Web3Error.revertCustom(customError.signature, errorArgs) + } else { + throw Web3Error.inputError(desc: "Signature matches \(customError.errorDeclaration) but failed to be decoded.") + } + } else { + throw Web3Error.inputError(desc: "Make sure ABI you use contains error that can match signature: 0x\(selector.toHexString())") + } + default: + throw Web3Error.inputError(desc: "Given data has invalid bytes count.") } - return methods[method]?.compactMap({ function in - return function.decodeReturnData(data) - }).first } public func decodeInputData(_ method: String, data: Data) -> [String: Any]? { @@ -346,8 +377,32 @@ extension DefaultContractProtocol { return function.decodeInputData(Data(data[data.startIndex + 4 ..< data.startIndex + data.count])) } + public func decodeEthError(_ data: Data) -> [String: Any]? { + guard data.count >= 4, + let err = errors.first(where: { $0.value.methodEncoding == data[0..<4] })?.value else { + return nil + } + return err.decodeEthError(data[4...]) + } + public func getFunctionCalled(_ data: Data) -> ABI.Element.Function? { guard data.count >= 4 else { return nil } return methods[data[data.startIndex ..< data.startIndex + 4].toHexString().addHexPrefix()]?.first } } + +extension DefaultContractProtocol { + @discardableResult + public func callStatic(_ method: String, parameters: [Any], provider: Web3Provider) async throws -> [String: Any] { + guard let address = address else { + throw Web3Error.inputError(desc: "RPC failed: contract is missing an address.") + } + guard let data = self.method(method, parameters: parameters, extraData: nil) else { + throw Web3Error.dataError + } + let transaction = CodableTransaction(to: address, data: data) + + let result: Data = try await APIRequest.sendRequest(with: provider, for: .call(transaction, .latest)).result + return try decodeReturnData(method, data: result) + } +} diff --git a/Sources/Web3Core/EthereumABI/ABIElements.swift b/Sources/Web3Core/EthereumABI/ABIElements.swift index f238e543f..4c58f08e7 100755 --- a/Sources/Web3Core/EthereumABI/ABIElements.swift +++ b/Sources/Web3Core/EthereumABI/ABIElements.swift @@ -202,7 +202,7 @@ extension ABI.Element.Constructor { extension ABI.Element.Function { /// Encode parameters of a given contract method - /// - Parameter parameters: Parameters to pass to Ethereum contract + /// - Parameters: Parameters to pass to Ethereum contract /// - Returns: Encoded data public func encodeParameters(_ parameters: [Any]) -> Data? { guard parameters.count == inputs.count, @@ -292,6 +292,44 @@ extension ABI.Element.Event { } } +// MARK: - Decode custom error + +extension ABI.Element.EthError { + /// Decodes `revert CustomError(_)` calls. + /// - Parameters: + /// - data: bytes returned by a function call that stripped error signature hash. + /// - Returns: a dictionary containing decoded data mappend to indices and names of returned values or nil if decoding failed. + public func decodeEthError(_ data: Data) -> [String: Any]? { + guard inputs.count * 32 <= data.count, + let decoded = ABIDecoder.decode(types: inputs, data: data) else { + return nil + } + + var result = [String: Any]() + for (index, out) in inputs.enumerated() { + result["\(index)"] = decoded[index] + if !out.name.isEmpty { + result[out.name] = decoded[index] + } + } + return result + } + + /// Decodes `revert(string)` or `require(expression, string)` calls. + /// These calls are decomposed as `Error(string)` error. + public static func decodeStringError(_ data: Data) -> String? { + let decoded = ABIDecoder.decode(types: [.init(name: "", type: .string)], data: data) + return decoded?.first as? String + } + + /// Decodes `Panic(uint256)` errors. + /// See more about panic code explain at: https://docs.soliditylang.org/en/v0.8.21/control-structures.html#panic-via-assert-and-error-via-require + public static func decodePanicError(_ data: Data) -> BigUInt? { + let decoded = ABIDecoder.decode(types: [.init(name: "", type: .uint(bits: 256))], data: data) + return decoded?.first as? BigUInt + } +} + // MARK: - Function input/output decoding extension ABI.Element { @@ -304,7 +342,7 @@ extension ABI.Element { case .fallback: return nil case .function(let function): - return function.decodeReturnData(data) + return try? function.decodeReturnData(data) case .receive: return nil case .error: @@ -337,74 +375,38 @@ extension ABI.Element.Function { return ABIDecoder.decodeInputData(rawData, methodEncoding: methodEncoding, inputs: inputs) } - /// Decodes data returned by a function call. Able to decode `revert(string)`, `revert CustomError(...)` and `require(expression, string)` calls. + /// Decodes data returned by a function call. /// - Parameters: /// - data: bytes returned by a function call; - /// - errors: optional dictionary of known errors that could be returned by the function you called. Used to decode the error information. /// - Returns: a dictionary containing decoded data mappend to indices and names of returned values if these are not `nil`. - /// If `data` is an error response returns dictionary containing all available information about that specific error. Read more for details. + /// - Throws: + /// - `Web3Error.processingError(desc: String)` when decode process failed. /// /// Return cases: - /// - when no `outputs` declared and `data` is not an error response: + /// - when no `outputs` declared: /// ```swift - /// ["_success": true] + /// [:] /// ``` /// - when `outputs` declared and decoding completed successfully: /// ```swift - /// ["_success": true, "0": value_1, "1": value_2, ...] + /// ["0": value_1, "1": value_2, ...] /// ``` /// Additionally this dictionary will have mappings to output names if these names are specified in the ABI; - /// - function call was aborted using `revert(message)` or `require(expression, message)`: - /// ```swift - /// ["_success": false, "_abortedByRevertOrRequire": true, "_errorMessage": message]` - /// ``` - /// - function call was aborted using `revert CustomMessage()` and `errors` argument contains the ABI of that custom error type: - /// ```swift - /// ["_success": false, - /// "_abortedByRevertOrRequire": true, - /// "_error": error_name_and_types, // e.g. `MyCustomError(uint256, address senderAddress)` - /// "0": error_arg1, - /// "1": error_arg2, - /// ..., - /// "error_arg1_name": error_arg1, // Only named arguments will be mapped to their names, e.g. `"senderAddress": EthereumAddress` - /// "error_arg2_name": error_arg2, // Otherwise, you can query them by position index. - /// ...] - /// ``` - /// - in case of any error: - /// ```swift - /// ["_success": false, "_failureReason": String] - /// ``` - /// Error reasons include: - /// - `outputs` declared but at least one value failed to be decoded; - /// - `data.count` is less than `outputs.count * 32`; - /// - `outputs` defined and `data` is empty; - /// - `data` represent reverted transaction - /// - /// How `revert(string)` and `require(expression, string)` return value is decomposed: - /// - `08C379A0` function selector for `Error(string)`; - /// - next 32 bytes are the data offset; - /// - next 32 bytes are the error message length; - /// - the next N bytes, where N >= 32, are the message bytes - /// - the rest are 0 bytes padding. - public func decodeReturnData(_ data: Data, errors: [String: ABI.Element.EthError]? = nil) -> [String: Any] { - if let decodedError = decodeErrorResponse(data, errors: errors) { - return decodedError - } - + public func decodeReturnData(_ data: Data) throws -> [String: Any] { guard !outputs.isEmpty else { NSLog("Function doesn't have any output types to decode given data.") - return ["_success": true] + return [:] } guard outputs.count * 32 <= data.count else { - return ["_success": false, "_failureReason": "Bytes count must be at least \(outputs.count * 32). Given \(data.count). Decoding will fail."] + throw Web3Error.processingError(desc: "Bytes count must be at least \(outputs.count * 32). Given \(data.count). Decoding will fail.") } // TODO: need improvement - we should be able to tell which value failed to be decoded guard let values = ABIDecoder.decode(types: outputs, data: data) else { - return ["_success": false, "_failureReason": "Failed to decode at least one value."] + throw Web3Error.processingError(desc: "Failed to decode at least one value.") } - var returnArray: [String: Any] = ["_success": true] + var returnArray: [String: Any] = [:] for i in outputs.indices { returnArray["\(i)"] = values[i] if !outputs[i].name.isEmpty { @@ -453,6 +455,7 @@ extension ABI.Element.Function { /// // "_parsingError" is optional and is present only if decoding of custom error arguments failed /// "_parsingError": "Data matches MyCustomError(uint256, address senderAddress) but failed to be decoded."] /// ``` + @available(*, deprecated, message: "Use decode function from `ABI.Element.EthError` instead") public func decodeErrorResponse(_ data: Data, errors: [String: ABI.Element.EthError]? = nil) -> [String: Any]? { /// If data is empty and outputs are expected it is treated as a `require(expression)` or `revert()` call with no message. /// In solidity `require(false)` and `revert()` calls return empty error response. diff --git a/Sources/Web3Core/EthereumABI/ABIParameterTypes.swift b/Sources/Web3Core/EthereumABI/ABIParameterTypes.swift index eb536237e..7abd04ed1 100755 --- a/Sources/Web3Core/EthereumABI/ABIParameterTypes.swift +++ b/Sources/Web3Core/EthereumABI/ABIParameterTypes.swift @@ -168,31 +168,79 @@ extension ABI.Element.ParameterType: Equatable { } extension ABI.Element.Function { + /// String representation of a function, e.g. `transfer(address,uint256)`. public var signature: String { return "\(name ?? "")(\(inputs.map { $0.type.abiRepresentation }.joined(separator: ",")))" } + /// Function selector, e.g. `"cafe1234"`. Without hex prefix `0x`. + @available(*, deprecated, renamed: "selector", message: "Please, use 'selector' property instead.") public var methodString: String { + return selector + } + + /// Function selector, e.g. `"cafe1234"`. Without hex prefix `0x`. + public var selector: String { return String(signature.sha3(.keccak256).prefix(8)) } + /// Function selector (e.g. `0xcafe1234`) but as raw bytes. + @available(*, deprecated, renamed: "selectorEncoded", message: "Please, use 'selectorEncoded' property instead.") public var methodEncoding: Data { - return signature.data(using: .ascii)!.sha3(.keccak256)[0...3] + return selectorEncoded + } + + /// Function selector (e.g. `0xcafe1234`) but as raw bytes. + public var selectorEncoded: Data { + return Data.fromHex(selector)! } } // MARK: - Event topic extension ABI.Element.Event { + /// String representation of an event, e.g. `ContractCreated(address)`. public var signature: String { return "\(name)(\(inputs.map { $0.type.abiRepresentation }.joined(separator: ",")))" } + /// Hashed signature of an event, e.g. `0xcf78cf0d6f3d8371e1075c69c492ab4ec5d8cf23a1a239b6a51a1d00be7ca312`. public var topic: Data { return signature.data(using: .ascii)!.sha3(.keccak256) } } +extension ABI.Element.EthError { + /// String representation of an error, e.g. `TrasferFailed(address)`. + public var signature: String { + return "\(name)(\(inputs.map { $0.type.abiRepresentation }.joined(separator: ",")))" + } + + /// Error selector, e.g. `"cafe1234"`. Without hex prefix `0x`. + @available(*, deprecated, renamed: "selector", message: "Please, use 'selector' property instead.") + public var methodString: String { + return selector + } + + /// Error selector, e.g. `"cafe1234"`. Without hex prefix `0x`. + public var selector: String { + return String(signature.sha3(.keccak256).prefix(8)) + } + + /// Error selector (e.g. `0xcafe1234`) but as raw bytes. + @available(*, deprecated, renamed: "selectorEncoded", message: "Please, use 'selectorEncoded' property instead.") + public var methodEncoding: Data { + return selectorEncoded + } + + /// Error selector (e.g. `0xcafe1234`) but as raw bytes. + public var selectorEncoded: Data { + return Data.fromHex(selector)! + } +} + extension ABI.Element.ParameterType: ABIEncoding { + + /// Returns a valid solidity type like `address`, `uint128` or any other built-in type from Solidity. public var abiRepresentation: String { switch self { case .uint(let bits): diff --git a/Sources/Web3Core/EthereumABI/Sequence+ABIExtension.swift b/Sources/Web3Core/EthereumABI/Sequence+ABIExtension.swift index 1d2f32744..390b928ee 100644 --- a/Sources/Web3Core/EthereumABI/Sequence+ABIExtension.swift +++ b/Sources/Web3Core/EthereumABI/Sequence+ABIExtension.swift @@ -56,6 +56,8 @@ public extension Sequence where Element == ABI.Element { var errors = [String: ABI.Element.EthError]() for case let .error(error) in self { errors[error.name] = error + errors[error.signature] = error + errors[error.methodString.addHexPrefix().lowercased()] = error } return errors } diff --git a/Sources/Web3Core/EthereumNetwork/Request/APIRequest+ComputedProperties.swift b/Sources/Web3Core/EthereumNetwork/Request/APIRequest+ComputedProperties.swift index cfa7f5190..fc711ce4b 100644 --- a/Sources/Web3Core/EthereumNetwork/Request/APIRequest+ComputedProperties.swift +++ b/Sources/Web3Core/EthereumNetwork/Request/APIRequest+ComputedProperties.swift @@ -8,15 +8,12 @@ import Foundation extension APIRequest { - var method: REST { + public var method: REST { .POST } - public var encodedBody: Data { - let request = RequestBody(method: call, params: parameters) - // this is safe to force try this here - // Because request must failed to compile if it not conformable with `Encodable` protocol - return try! JSONEncoder().encode(request) + public var encodedBody: Data { + RequestBody(method: call, params: parameters).encodedBody } var parameters: [RequestParameter] { diff --git a/Sources/Web3Core/EthereumNetwork/Request/APIRequest+Methods.swift b/Sources/Web3Core/EthereumNetwork/Request/APIRequest+Methods.swift index 58e13aa0f..c25ee496d 100644 --- a/Sources/Web3Core/EthereumNetwork/Request/APIRequest+Methods.swift +++ b/Sources/Web3Core/EthereumNetwork/Request/APIRequest+Methods.swift @@ -8,22 +8,116 @@ import Foundation import BigInt +/// TODO: should we do more error explain like ethers.js? +/// https://github.com/ethers-io/ethers.js/blob/0bfa7f497dc5793b66df7adfb42c6b846c51d794/packages/providers/src.ts/json-rpc-provider.ts#L55 +func checkError(method: String, error: JsonRpcErrorObject.RpcError) throws -> String { + if method == "eth_call" { + if let result = spelunkData(value: error) { + return result.data + } + throw Web3Error.nodeError(desc: "Error data decoding failed: missing revert data in exception; Transaction reverted without a reason string.") + } + + throw Web3Error.nodeError(desc: error.message) +} + +func spelunkData(value: Any?) -> (message: String, data: String)? { + if (value == nil) { + return nil + } + + func spelunkRpcError(_ message: String, data: String) -> (message: String, data: String)? { + if message.contains("revert") && data.isHex { + return (message, data) + } else { + return nil + } + } + + if let error = value as? JsonRpcErrorObject.RpcError { + if let data = error.data as? String { + return spelunkRpcError(error.message, data: data) + } else { + return spelunkData(value: error.data) + } + } + + // Spelunk further... + if let object = value as? [String: Any] { + if let message = object["message"] as? String, + let data = object["data"] as? String { + return spelunkRpcError(message, data: data) + } + + for value in object.values { + if let result = spelunkData(value: value) { + return result + } + return nil + } + } + if let array = value as? [Any] { + for e in array { + if let result = spelunkData(value: e) { + return result + } + return nil + } + } + + // Might be a JSON string we can further descend... + if let string = value as? String, let data = string.data(using: .utf8) { + let json = try? JSONSerialization.jsonObject(with: data) + return spelunkData(value: json) + } + + return nil +} + extension APIRequest { public static func sendRequest(with provider: Web3Provider, for call: APIRequest) async throws -> APIResponse { - let request = setupRequest(for: call, with: provider) - return try await APIRequest.send(uRLRequest: request, with: provider.session) + try await send(call.call, parameters: call.parameters, with: provider) } - static func setupRequest(for call: APIRequest, with provider: Web3Provider) -> URLRequest { + static func setupRequest(for body: RequestBody, with provider: Web3Provider) -> URLRequest { var urlRequest = URLRequest(url: provider.url, cachePolicy: .reloadIgnoringCacheData) urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") urlRequest.setValue("application/json", forHTTPHeaderField: "Accept") - urlRequest.httpMethod = call.method.rawValue - urlRequest.httpBody = call.encodedBody + urlRequest.httpMethod = "POST" + urlRequest.httpBody = body.encodedBody return urlRequest } - public static func send(uRLRequest: URLRequest, with session: URLSession) async throws -> APIResponse { + public static func send(_ method: String, parameters: [Encodable], with provider: Web3Provider) async throws -> APIResponse { + let body = RequestBody(method: method, params: parameters) + let uRLRequest = setupRequest(for: body, with: provider) + + let data: Data + do { + data = try await send(uRLRequest: uRLRequest, with: provider.session) + } catch Web3Error.rpcError(let error) { + let responseAsString = try checkError(method: method, error: error) + guard let LiteralType = Result.self as? LiteralInitiableFromString.Type, + let literalValue = LiteralType.init(from: responseAsString), + let result = literalValue as? Result else { + throw Web3Error.dataError + } + return APIResponse(id: 2, result: result) + } + + /// Checks if `Result` type can be initialized from HEX-encoded bytes. + /// If it can - we attempt initializing a value of `Result` type. + if let LiteralType = Result.self as? LiteralInitiableFromString.Type { + guard let responseAsString = try? JSONDecoder().decode(APIResponse.self, from: data) else { throw Web3Error.dataError } + guard let literalValue = LiteralType.init(from: responseAsString.result) else { throw Web3Error.dataError } + /// `literalValue` conforms `LiteralInitiableFromString` (which conforms to an `APIResponseType` type) so it never fails. + guard let result = literalValue as? Result else { throw Web3Error.typeError } + return APIResponse(id: responseAsString.id, jsonrpc: responseAsString.jsonrpc, result: result) + } + return try JSONDecoder().decode(APIResponse.self, from: data) + } + + public static func send(uRLRequest: URLRequest, with session: URLSession) async throws -> Data { let (data, response) = try await session.data(for: uRLRequest) guard 200 ..< 400 ~= response.statusCode else { @@ -34,9 +128,9 @@ extension APIRequest { } } - if let error = (try? JSONDecoder().decode(JsonRpcErrorObject.self, from: data))?.error { + if let error = JsonRpcErrorObject.init(from: data)?.error { guard let parsedErrorCode = error.parsedErrorCode else { - throw Web3Error.nodeError(desc: "\(error.message)\nError code: \(error.code)") + throw Web3Error.rpcError(error) } let description = "\(parsedErrorCode.errorName). Error code: \(error.code). \(error.message)" switch parsedErrorCode { @@ -49,35 +143,51 @@ extension APIRequest { } } - /// This bit of code is purposed to work with literal types that comes in ``Response`` in hexString type. - /// Currently it's just `Data` and any kind of Integers `(U)Int`, `Big(U)Int`. - if let LiteralType = Result.self as? LiteralInitiableFromString.Type { - guard let responseAsString = try? JSONDecoder().decode(APIResponse.self, from: data) else { throw Web3Error.dataError } - guard let literalValue = LiteralType.init(from: responseAsString.result) else { throw Web3Error.dataError } - /// `literalValue` conforms `LiteralInitiableFromString`, that conforming to an `APIResponseType` type, so it's never fails. - guard let result = literalValue as? Result else { throw Web3Error.typeError } - return APIResponse(id: responseAsString.id, jsonrpc: responseAsString.jsonrpc, result: result) - } - return try JSONDecoder().decode(APIResponse.self, from: data) + return data } } /// JSON RPC Error object. See official specification https://www.jsonrpc.org/specification#error_object -private struct JsonRpcErrorObject: Decodable { +public struct JsonRpcErrorObject { public let error: RpcError? - class RpcError: Decodable { - let message: String - let code: Int + public class RpcError { + public let message: String + public let code: Int + public let data: Any? + + init(message: String, code: Int, data: Any?) { + self.message = message + self.code = code + self.data = data + } + var parsedErrorCode: JsonRpcErrorCode? { JsonRpcErrorCode.from(code) } } + + init?(from data: Data) { + guard let root = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { + return nil + } + if let error = root["error"] as? [String: Any], + let message = error["message"] as? String, + let code = error["code"] as? Int { + guard let errorData = error["data"] else { + self.error = RpcError(message: message, code: code, data: nil) + return + } + self.error = RpcError(message: message, code: code, data: errorData) + } else { + self.error = nil + } + } } /// For error codes specification see chapter `5.1 Error object` /// https://www.jsonrpc.org/specification#error_object -private enum JsonRpcErrorCode { +enum JsonRpcErrorCode { /// -32700 /// Invalid JSON was received by the server. An error occurred on the server while parsing the JSON case parseError diff --git a/Sources/Web3Core/EthereumNetwork/Request/APIRequest+UtilityTypes.swift b/Sources/Web3Core/EthereumNetwork/Request/APIRequest+UtilityTypes.swift index ca55d622f..1e40e53d5 100644 --- a/Sources/Web3Core/EthereumNetwork/Request/APIRequest+UtilityTypes.swift +++ b/Sources/Web3Core/EthereumNetwork/Request/APIRequest+UtilityTypes.swift @@ -14,7 +14,7 @@ public struct APIResponse: Decodable where Result: APIResultType { public var result: Result } -enum REST: String { +public enum REST: String { case POST case GET } @@ -24,5 +24,30 @@ struct RequestBody: Encodable { var id = Counter.increment() var method: String - var params: [RequestParameter] + var params: [Encodable] + + enum CodingKeys: String, CodingKey { + case jsonrpc + case id + case method + case params + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(jsonrpc, forKey: .jsonrpc) + try container.encode(id, forKey: .id) + try container.encode(method, forKey: .method) + + var paramsContainer = container.superEncoder(forKey: .params).unkeyedContainer() + try params.forEach { a in + try paramsContainer.encode(a) + } + } + + public var encodedBody: Data { + // Safe to use force-try because request will fail to + // compile if it's not conforming to the `Encodable` protocol. + return try! JSONEncoder().encode(self) + } } diff --git a/Sources/Web3Core/Utility/String+Extension.swift b/Sources/Web3Core/Utility/String+Extension.swift index 529fe2ff0..778558166 100755 --- a/Sources/Web3Core/Utility/String+Extension.swift +++ b/Sources/Web3Core/Utility/String+Extension.swift @@ -120,7 +120,7 @@ extension String { let to16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location + nsRange.length, limitedBy: utf16.endIndex), let from = from16.samePosition(in: self), let to = to16.samePosition(in: self) - else { return nil } + else { return nil } return from ..< to } @@ -136,25 +136,39 @@ extension String { trimmingCharacters(in: .whitespacesAndNewlines) } - /// Splits a string into groups of `every` n characters, grouping from left-to-right by default. If `backwards` is true, right-to-left. - public func split(every: Int, backwards: Bool = false) -> [String] { - var result = [String]() - - for i in stride(from: 0, to: self.count, by: every) { - switch backwards { - case true: - let endIndex = self.index(self.endIndex, offsetBy: -i) - let startIndex = self.index(endIndex, offsetBy: -every, limitedBy: self.startIndex) ?? self.startIndex - result.insert(String(self[startIndex.. [String] { + var result = [String]() + + for i in stride(from: 0, to: self.count, by: every) { + switch backwards { + case true: + let endIndex = self.index(self.endIndex, offsetBy: -i) + let startIndex = self.index(endIndex, offsetBy: -every, limitedBy: self.startIndex) ?? self.startIndex + result.insert(String(self[startIndex.. [String: Any] { + try await contract.callStatic(method, parameters: parameters, provider: web3.provider) + } } } diff --git a/Sources/web3swift/Web3/Web3+HttpProvider.swift b/Sources/web3swift/Web3/Web3+HttpProvider.swift index a6411552d..1e75b242c 100755 --- a/Sources/web3swift/Web3/Web3+HttpProvider.swift +++ b/Sources/web3swift/Web3/Web3+HttpProvider.swift @@ -33,13 +33,21 @@ public class Web3HttpProvider: Web3Provider { if let net = net { network = net } else { - var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData) - urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") - urlRequest.setValue("application/json", forHTTPHeaderField: "Accept") - urlRequest.httpMethod = APIRequest.getNetwork.call - urlRequest.httpBody = APIRequest.getNetwork.encodedBody - let response: APIResponse = try await APIRequest.send(uRLRequest: urlRequest, with: session) - self.network = Networks.fromInt(response.result) + /// chain id could be a hex string or an int value. + let response: String = try await APIRequest.send(APIRequest.getNetwork.call, parameters: [], with: self).result + let result: UInt + if response.hasHexPrefix() { + guard let num = BigUInt(response, radix: 16) else { + throw Web3Error.processingError(desc: "Get network succeeded but can't be parsed to a valid chain id.") + } + result = UInt(num) + } else { + guard let num = UInt(response) else { + throw Web3Error.processingError(desc: "Get network succeeded but can't be parsed to a valid chain id.") + } + result = num + } + self.network = Networks.fromInt(result) } attachedKeystoreManager = manager } diff --git a/Tests/web3swiftTests/localTests/ABIDecoderSliceTests.swift b/Tests/web3swiftTests/localTests/ABIDecoderSliceTests.swift index a3fbeb43c..3e7f077d8 100644 --- a/Tests/web3swiftTests/localTests/ABIDecoderSliceTests.swift +++ b/Tests/web3swiftTests/localTests/ABIDecoderSliceTests.swift @@ -27,7 +27,7 @@ final class ABIDecoderSliceTests: XCTestCase { while startIndex < data.count { let slice = data[startIndex ..< startIndex + answerSize] startIndex += answerSize - guard let bigInt = balanceofMethod.decodeReturnData(slice)["0"] as? BigUInt else { + guard let bigInt = try balanceofMethod.decodeReturnData(slice)["0"] as? BigUInt else { throw Web3Error.processingError(desc: "Can not decode returned parameters") } let value = Utilities.formatToPrecision(bigInt, units: .wei) @@ -52,9 +52,7 @@ final class ABIDecoderSliceTests: XCTestCase { XCTAssertEqual(methods.count, 3) /// Act - guard let decodedData = multiCall2Contract.decodeReturnData("aggregate", data: data) else { - throw Web3Error.processingError(desc: "Can not decode returned parameters") - } + let decodedData = try multiCall2Contract.decodeReturnData("aggregate", data: data) guard let returnData = decodedData["returnData"] as? [Data] else { throw Web3Error.dataError @@ -63,7 +61,7 @@ final class ABIDecoderSliceTests: XCTestCase { XCTAssertEqual(returnData.count, 3) for item in methods.enumerated() { - XCTAssertNotNil(item.element.decodeReturnData(returnData[item.offset])["0"]) + XCTAssertNotNil(try item.element.decodeReturnData(returnData[item.offset])["0"]) } } @@ -74,9 +72,7 @@ final class ABIDecoderSliceTests: XCTestCase { let erc20_balanceof = try EthereumContract(Web3.Utils.erc20ABI).methods["balanceOf"]!.first! /// Act - guard let decodedData = contract.decodeReturnData("tryAggregate", data: data) else { - throw Web3Error.processingError(desc: "Can not decode returned parameters") - } + let decodedData = try contract.decodeReturnData("tryAggregate", data: data) guard let returnData = decodedData["returnData"] as? [[Any]] else { throw Web3Error.dataError @@ -84,7 +80,7 @@ final class ABIDecoderSliceTests: XCTestCase { var resultArray = [BigUInt]() for i in 0..<2 { guard let data = returnData[i][1] as? Data, - let balance = erc20_balanceof.decodeReturnData(data)["0"] as? BigUInt else { + let balance = try? erc20_balanceof.decodeReturnData(data)["0"] as? BigUInt else { resultArray.append(0) continue } diff --git a/Tests/web3swiftTests/localTests/ABIElementErrorDecodingTest.swift b/Tests/web3swiftTests/localTests/ABIElementErrorDecodingTest.swift index 181337d19..d0aa1b9d8 100644 --- a/Tests/web3swiftTests/localTests/ABIElementErrorDecodingTest.swift +++ b/Tests/web3swiftTests/localTests/ABIElementErrorDecodingTest.swift @@ -65,123 +65,140 @@ class ABIElementErrorDecodingTest: XCTestCase { XCTAssertTrue(emptyFunction.decodeErrorResponse(Data()) == nil) } - func testDecodeEmptyErrorOnOneOutputFunction() { - guard let errorData = oneOutputFunction.decodeErrorResponse(Data()) else { - XCTFail("Empty Data must be decoded as a `revert()` or `require(false)` call if function used to decode it has at least one output parameter.") - return + /// `require(expression)` and `revert()` without a message return 0 bytes, + /// we can noly catch an error when function has a return value + func testDecodeEmptyErrorOnOneOutputFunction() throws { + let contract = try EthereumContract(abi: [.function(emptyFunction)]) + do { + try contract.decodeReturnData(emptyFunction.signature, data: Data()) + } catch { + XCTFail() } - XCTAssertEqual(errorData["_success"] as? Bool, false) - XCTAssertNotNil(errorData["_failureReason"] as? String) - - let decodedOutput = oneOutputFunction.decodeReturnData(Data()) - - XCTAssertEqual(errorData["_success"] as? Bool, decodedOutput["_success"] as? Bool) - XCTAssertEqual(errorData["_failureReason"] as? String, decodedOutput["_failureReason"] as? String) + let contract2 = try EthereumContract(abi: [.function(oneOutputFunction)]) + do { + try contract2.decodeReturnData(oneOutputFunction.signature, data: Data()) + XCTFail() + } catch { + print(error) + } } /// Data is decoded as a call of `revert` or `require` with a message no matter the number of outputs configured in the ``ABI/Element/Function``. /// `revert(message)` and `require(false,message)`return at least 128 bytes. We cannot differentiate between `require` or `revert`. - func testDecodeDefaultErrorWithMessage() { + func testDecodeDefaultErrorWithMessage() throws { /// 08c379a0 - Error(string) function selector /// 0000000000000000000000000000000000000000000000000000000000000020 - Data offset /// 000000000000000000000000000000000000000000000000000000000000001a - Message length /// 4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 - Message + 0 bytes padding /// 0000... - some more 0 bytes padding to make the number of bytes match 32 bytes chunks - let errorResponse = Data.fromHex("08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a4e6f7420656e6f7567682045746865722070726f76696465642e00000000000000000000000000000000000000000000000000000000000000000000")! - guard let errorData = emptyFunction.decodeErrorResponse(errorResponse) else { - XCTFail("Data must be decoded as a `revert(\"Not enough Ether provided.\")` or `require(false, \"Not enough Ether provided.\")` but decoding failed completely.") - return + let errorResponse = Data.fromHex("08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a4e6f7420656e6f7567682045746865722070726f76696465642e0000000000000000000000000000000000000000000000000000000000000000000000000000")! + let contract = try EthereumContract(abi: [.function(emptyFunction)]) + + do { + try contract.decodeReturnData(emptyFunction.signature, data: errorResponse) + XCTFail("decode function should throw an error") + } catch Web3Error.revert(_, let reason) { + XCTAssertEqual(reason, "Not enough Ether provided.") } - XCTAssertEqual(errorData["_success"] as? Bool, false) - XCTAssertEqual(errorData["_abortedByRevertOrRequire"] as? Bool, true) - XCTAssertEqual(errorData["_errorMessage"] as? String, "Not enough Ether provided.") - XCTAssertNotNil(errorData["_failureReason"] as? String) - - let decodedOutput = oneOutputFunction.decodeReturnData(errorResponse) - - XCTAssertEqual(errorData["_success"] as? Bool, decodedOutput["_success"] as? Bool) - XCTAssertEqual(errorData["_failureReason"] as? String, decodedOutput["_failureReason"] as? String) - XCTAssertEqual(errorData["_abortedByRevertOrRequire"] as? Bool, decodedOutput["_abortedByRevertOrRequire"] as? Bool) - XCTAssertEqual(errorData["_errorMessage"] as? String, decodedOutput["_errorMessage"] as? String) - XCTAssertEqual(decodedOutput["_errorMessage"] as? String, "Not enough Ether provided.") + XCTAssertEqual(EthError.decodeStringError(errorResponse[4...]), "Not enough Ether provided.") } - /// Data is decoded as a call of `revert Unauthorized()`. Decoded only if custom error ABI is given. - func testDecodeRevertWithCustomError() { + /// Data is decoded as a call of `revert Unauthorized()` + func testDecodeRevertWithCustomError() throws { /// 82b42900 - Unauthorized() function selector /// 00000000000000000000000000000000000000000000000000000000 - padding bytes - let errorResponse = Data.fromHex("82b4290000000000000000000000000000000000000000000000000000000000")! - let errors: [String: EthError] = ["82b42900": .init(name: "Unauthorized", inputs: [])] - guard let errorData = emptyFunction.decodeErrorResponse(errorResponse, errors: errors) else { - XCTFail("Data must be decoded as a `revert(\"Not enough Ether provided.\")` or `require(false, \"Not enough Ether provided.\")` but decoding failed completely.") - return + let errorResponse = Data.fromHex("82b429000000000000000000000000000000000000000000000000000000000000000000")! + let error = ABI.Element.EthError(name: "Unauthorized", inputs: []) + let contract = try EthereumContract(abi: [.function(emptyFunction), .error(error)] ) + + do { + try contract.decodeReturnData(emptyFunction.signature, data: errorResponse) + XCTFail("decode function should throw an error") + } catch Web3Error.revertCustom(let signature, let args) { + XCTAssertEqual(signature, "Unauthorized()") + XCTAssertTrue(args.isEmpty) } - XCTAssertEqual(errorData["_success"] as? Bool, false) - XCTAssertEqual(errorData["_abortedByRevertOrRequire"] as? Bool, true) - XCTAssertEqual(errorData["_error"] as? String, "Unauthorized()") - - let decodedOutput = oneOutputFunction.decodeReturnData(errorResponse, errors: errors) - - XCTAssertEqual(errorData["_success"] as? Bool, decodedOutput["_success"] as? Bool) - XCTAssertEqual(errorData["_abortedByRevertOrRequire"] as? Bool, decodedOutput["_abortedByRevertOrRequire"] as? Bool) - XCTAssertEqual(errorData["_error"] as? String, decodedOutput["_error"] as? String) + guard let decoded = error.decodeEthError(errorResponse[4...]) else { + XCTFail("decode response failed.") + return + } + XCTAssertTrue(decoded.isEmpty) } - /// Data is decoded as a call of `revert Unauthorized()`. Decoded only if custom error ABI is given. + /// Data is decoded as a call of `revert Unauthorized(bool)`. /// Trying to decode as `Unauthorized(string)`. Must fail. - func testDecodeRevertWithCustomErrorFailed() { - /// 82b42900 - Unauthorized() function selector + func testDecodeRevertWithCustomErrorFailed() throws { + /// 5caef992 - Unauthorized(bool) function selector /// 00000000000000000000000000000000000000000000000000000000 - padding bytes - let errorResponse = Data.fromHex("82b4290000000000000000000000000000000000000000000000000000000000")! - let errors: [String: EthError] = ["82b42900": .init(name: "Unauthorized", inputs: [.init(name: "", type: .string)])] - guard let errorData = emptyFunction.decodeErrorResponse(errorResponse, errors: errors) else { - XCTFail("Data must be decoded as a `revert(\"Not enough Ether provided.\")` or `require(false, \"Not enough Ether provided.\")` but decoding failed completely.") - return + let errorResponse = Data.fromHex("5caef9920000000000000000000000000000000000000000000000000000000000000000")! + let error = ABI.Element.EthError(name: "Unauthorized", inputs: [.init(name: "", type: .bool)]) + let contract = try EthereumContract(abi: [.function(oneOutputFunction), .error(error)] ) + + do { + try contract.decodeReturnData(oneOutputFunction.signature, data: errorResponse) + XCTFail("decode function should throw an error") + } catch Web3Error.revertCustom(let signature, let args) { + XCTAssertEqual(signature, "Unauthorized(bool)") + XCTAssertEqual(args["0"] as? Bool, false) } - XCTAssertEqual(errorData["_success"] as? Bool, false) - XCTAssertEqual(errorData["_abortedByRevertOrRequire"] as? Bool, true) - XCTAssertEqual(errorData["_error"] as? String, "Unauthorized(string)") - XCTAssertEqual(errorData["_parsingError"] as? String, "Data matches Unauthorized(string) but failed to be decoded.") - - let decodedOutput = oneOutputFunction.decodeReturnData(errorResponse, errors: errors) - - XCTAssertEqual(errorData["_success"] as? Bool, decodedOutput["_success"] as? Bool) - XCTAssertEqual(errorData["_abortedByRevertOrRequire"] as? Bool, decodedOutput["_abortedByRevertOrRequire"] as? Bool) - XCTAssertEqual(errorData["_error"] as? String, decodedOutput["_error"] as? String) - XCTAssertEqual(errorData["_parsingError"] as? String, decodedOutput["_parsingError"] as? String) + guard let decoded = error.decodeEthError(errorResponse[4...]) else { + XCTFail("decode response failed.") + return + } + XCTAssertEqual(decoded["0"] as? Bool, false) } /// Data is decoded as a call of `revert Unauthorized("Reason")`. Decoded only if custom error ABI is given. /// The custom error argument must be extractable by index and name if the name is available. - func testDecodeRevertWithCustomErrorWithArguments() { + func testDecodeRevertWithCustomErrorWithArguments() throws { /// 973d02cb - `Unauthorized(string)` function selector /// 0000000000000000000000000000000000000000000000000000000000000020 - data offset /// 0000000000000000000000000000000000000000000000000000000000000006 - first custom argument length /// 526561736f6e0000000000000000000000000000000000000000000000000000 - first custom argument bytes + 0 bytes padding /// 0000... - some more 0 bytes padding to make the number of bytes match 32 bytes chunks - let errorResponse = Data.fromHex("973d02cb00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006526561736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")! - let errors: [String: EthError] = ["973d02cb": .init(name: "Unauthorized", inputs: [.init(name: "message_arg", type: .string)])] - guard let errorData = emptyFunction.decodeErrorResponse(errorResponse, errors: errors) else { - XCTFail("Data must be decoded as a `revert(\"Not enough Ether provided.\")` or `require(false, \"Not enough Ether provided.\")` but decoding failed completely.") - return + let errorResponse = Data.fromHex("973d02cb00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006526561736f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")! + let error = ABI.Element.EthError(name: "Unauthorized", inputs: [.init(name: "message_arg", type: .string)]) + let contract = try EthereumContract(abi: [.function(emptyFunction), .error(error)]) + + do { + try contract.decodeReturnData(emptyFunction.signature, data: errorResponse) + XCTFail("decode function should throw an error") + } catch Web3Error.revertCustom(let signature, let args) { + XCTAssertEqual(signature, "Unauthorized(string)") + XCTAssertEqual(args["0"] as? String, "Reason") + XCTAssertEqual(args["message_arg"] as? String, "Reason") } - XCTAssertEqual(errorData["_success"] as? Bool, false) - XCTAssertEqual(errorData["_abortedByRevertOrRequire"] as? Bool, true) - XCTAssertEqual(errorData["_error"] as? String, "Unauthorized(string message_arg)") - XCTAssertEqual(errorData["0"] as? String, "Reason") - XCTAssertEqual(errorData["0"] as? String, errorData["message_arg"] as? String) + guard let decoded = error.decodeEthError(errorResponse[4...]) else { + XCTFail("decode response failed.") + return + } + XCTAssertEqual(decoded["0"] as? String, "Reason") + XCTAssertEqual(decoded["message_arg"] as? String, "Reason") + } - let decodedOutput = oneOutputFunction.decodeReturnData(errorResponse, errors: errors) + /// Data is decoded as a panic exception is generated. + /// Example: + /// ``` solidity + /// function panicError() public { + /// assert(false); + /// } + /// ``` + func testDecodePanicError() throws { + let errorResponse = Data(hex: "4e487b710000000000000000000000000000000000000000000000000000000000000001") + let contract = try EthereumContract(abi: [.function(emptyFunction)]) + + do { + try contract.decodeReturnData(emptyFunction.signature, data: errorResponse) + } catch Web3Error.revert(let message, let code) { + XCTAssertTrue(message.contains("reverted with panic code 0x01")) + XCTAssertEqual(code, "0x01") + } - XCTAssertEqual(errorData["_success"] as? Bool, decodedOutput["_success"] as? Bool) - XCTAssertEqual(errorData["_abortedByRevertOrRequire"] as? Bool, decodedOutput["_abortedByRevertOrRequire"] as? Bool) - XCTAssertEqual(errorData["_error"] as? String, decodedOutput["_error"] as? String) - XCTAssertEqual(errorData["0"] as? String, decodedOutput["0"] as? String) - XCTAssertEqual(errorData["message_arg"] as? String, decodedOutput["message_arg"] as? String) + XCTAssertEqual(EthError.decodePanicError(errorResponse[4...]), 1) } } diff --git a/Tests/web3swiftTests/localTests/ABIElementsTests.swift b/Tests/web3swiftTests/localTests/ABIElementsTests.swift new file mode 100644 index 000000000..a94fa3d9d --- /dev/null +++ b/Tests/web3swiftTests/localTests/ABIElementsTests.swift @@ -0,0 +1,29 @@ +// +// ABIElementsTests.swift +// +// +// Created by JeneaVranceanu on 09.01.2024. +// + +import Foundation +import XCTest +@testable import web3swift +@testable import Web3Core + +class ABIElementsTests: XCTestCase { + + func testABIElementFunction() { + let test1Function = ABI.Element.Function(name: "Test1", + inputs: [], + outputs: [], + constant: true, + payable: false) + + XCTAssertEqual(test1Function.name, "Test1") + XCTAssertEqual(test1Function.selector, String("Test1()".sha3(.keccak256).prefix(8))) + XCTAssertEqual(test1Function.selectorEncoded, + Data.fromHex("Test1()".sha3(.keccak256))?.prefix(4)) + + } + +} diff --git a/Tests/web3swiftTests/localTests/AdvancedABIv2Tests.swift b/Tests/web3swiftTests/localTests/AdvancedABIv2Tests.swift index 066a1b7a8..e3e9f8cc1 100755 --- a/Tests/web3swiftTests/localTests/AdvancedABIv2Tests.swift +++ b/Tests/web3swiftTests/localTests/AdvancedABIv2Tests.swift @@ -17,20 +17,7 @@ class AdvancedABIv2Tests: LocalTestCase { let bytecode = Data.fromHex("6080604052341561000f57600080fd5b610cb18061001e6000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063189533931461007257806338163ff51461009b57806388be6c65146100c4578063e7c1a47c146100ed578063f376e01314610116575b600080fd5b341561007d57600080fd5b61008561013f565b6040516100929190610a9f565b60405180910390f35b34156100a657600080fd5b6100ae610220565b6040516100bb9190610a7d565b60405180910390f35b34156100cf57600080fd5b6100d76102c5565b6040516100e49190610ae3565b60405180910390f35b34156100f857600080fd5b610100610350565b60405161010d9190610ac1565b60405180910390f35b341561012157600080fd5b610129610399565b6040516101369190610b05565b60405180910390f35b6060600260405190808252806020026020018201604052801561017657816020015b60608152602001906001900390816101615790505b5090506040805190810160405280600581526020017f48656c6c6f0000000000000000000000000000000000000000000000000000008152508160008151811015156101be57fe5b906020019060200201819052506040805190810160405280600581526020017f576f726c6400000000000000000000000000000000000000000000000000000081525081600181518110151561021057fe5b9060200190602002018190525090565b610228610546565b6040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081600060028110151561026d57fe5b60200201819052506040805190810160405280600581526020017f576f726c640000000000000000000000000000000000000000000000000000008152508160016002811015156102ba57fe5b602002018190525090565b6060600260405190808252806020026020018201604052801561030257816020015b6102ef61056d565b8152602001906001900390816102e75790505b50905061030d610399565b81600081518110151561031c57fe5b90602001906020020181905250610331610399565b81600181518110151561034057fe5b9060200190602002018190525090565b6103586105a9565b610360610399565b81600060028110151561036f57fe5b602002018190525061037f610399565b81600160028110151561038e57fe5b602002018190525090565b6103a16105d8565b60606103ab610614565b6103b3610546565b60036040519080825280602002602001820160405280156103e35781602001602082028038833980820191505090505b50925060008360008151811015156103f757fe5b9060200190602002018181525050600183600181518110151561041657fe5b9060200190602002018181525050600283600281518110151561043557fe5b90602001906020020181815250506040805190810160405280600081526020016001815250915060408051908101604052806040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081526020016040805190810160405280600581526020017f576f726c64000000000000000000000000000000000000000000000000000000815250815250905060a060405190810160405280600181526020016040805190810160405280600b81526020017f48656c6c6f20776f726c64000000000000000000000000000000000000000000815250815260200183815260200184815260200182815250935083935050505090565b60408051908101604052806002905b60608152602001906001900390816105555790505090565b60e060405190810160405280600081526020016060815260200161058f610636565b8152602001606081526020016105a3610658565b81525090565b6101c0604051908101604052806002905b6105c261056d565b8152602001906001900390816105ba5790505090565b60e06040519081016040528060008152602001606081526020016105fa610636565b81526020016060815260200161060e610658565b81525090565b6040805190810160405280600290602082028038833980820191505090505090565b6040805190810160405280600290602082028038833980820191505090505090565b60408051908101604052806002905b60608152602001906001900390816106675790505090565b600061068a82610b81565b8360208202850161069a85610b31565b60005b848110156106d35783830388526106b5838351610930565b92506106c082610bdb565b915060208801975060018101905061069d565b508196508694505050505092915050565b60006106ef82610b76565b836020820285016106ff85610b27565b60005b8481101561073857838303885261071a838351610930565b925061072582610bce565b9150602088019750600181019050610702565b508196508694505050505092915050565b600061075482610b8c565b8084526020840193508360208202850161076d85610b3b565b60005b848110156107a6578383038852610788838351610930565b925061079382610be8565b9150602088019750600181019050610770565b508196508694505050505092915050565b60006107c282610b97565b836020820285016107d285610b48565b60005b8481101561080b5783830388526107ed8383516109ea565b92506107f882610bf5565b91506020880197506001810190506107d5565b508196508694505050505092915050565b600061082782610ba2565b8084526020840193508360208202850161084085610b52565b60005b8481101561087957838303885261085b8383516109ea565b925061086682610c02565b9150602088019750600181019050610843565b508196508694505050505092915050565b61089381610bad565b61089c82610b5f565b60005b828110156108ce576108b2858351610a6e565b6108bb82610c0f565b915060208501945060018101905061089f565b5050505050565b60006108e082610bb8565b8084526020840193506108f283610b69565b60005b8281101561092457610908868351610a6e565b61091182610c1c565b91506020860195506001810190506108f5565b50849250505092915050565b600061093b82610bc3565b80845261094f816020860160208601610c33565b61095881610c66565b602085010191505092915050565b600060c08301600083015161097e6000860182610a6e565b50602083015184820360208601526109968282610930565b91505060408301516109ab604086018261088a565b50606083015184820360808601526109c382826108d5565b915050608083015184820360a08601526109dd82826106e4565b9150508091505092915050565b600060c083016000830151610a026000860182610a6e565b5060208301518482036020860152610a1a8282610930565b9150506040830151610a2f604086018261088a565b5060608301518482036080860152610a4782826108d5565b915050608083015184820360a0860152610a6182826106e4565b9150508091505092915050565b610a7781610c29565b82525050565b60006020820190508181036000830152610a97818461067f565b905092915050565b60006020820190508181036000830152610ab98184610749565b905092915050565b60006020820190508181036000830152610adb81846107b7565b905092915050565b60006020820190508181036000830152610afd818461081c565b905092915050565b60006020820190508181036000830152610b1f8184610966565b905092915050565b6000819050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b600060029050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000819050919050565b60005b83811015610c51578082015181840152602081019050610c36565b83811115610c60576000848401525b50505050565b6000601f19601f83011690509190505600a265627a7a72305820fdaf8ce6fe282a46498c8066d5b5ac382b69969eee38e1ad03c0d501b27f65366c6578706572696d656e74616cf50037")! let web3 = try await Web3.new(LocalTestCase.url) - let allAddresses = try await web3.eth.ownedAccounts() - var contract = web3.contract(abiString, at: nil, abiVersion: 2)! - - // MARK: Writing Data flow - let deployTx = contract.prepareDeploy(bytecode: bytecode)! - deployTx.transaction.from = allAddresses[0] - // MARK: Sending Data flow - let policies = Policies(gasLimitPolicy: .manual(3000000)) - let result = try await deployTx.writeToChain(password: "web3swift", policies: policies, sendRaw: false) - let txHash = result.hash.stripHexPrefix() - - Thread.sleep(forTimeInterval: 1.0) - - let receipt = try await web3.eth.transactionReceipt(Data.fromHex(txHash)!) + let receipt = try await deployContract(bytecode: bytecode, abiString: abiString) switch receipt.status { case .notYetProcessed: @@ -39,7 +26,7 @@ class AdvancedABIv2Tests: LocalTestCase { break } - contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! + let contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! // MARK: Read data from ABI flow // MARK: - Encoding ABI Data flow let tx = contract.createReadOperation("testSingle") @@ -51,20 +38,7 @@ class AdvancedABIv2Tests: LocalTestCase { let bytecode = Data.fromHex("6080604052341561000f57600080fd5b610cb18061001e6000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063189533931461007257806338163ff51461009b57806388be6c65146100c4578063e7c1a47c146100ed578063f376e01314610116575b600080fd5b341561007d57600080fd5b61008561013f565b6040516100929190610a9f565b60405180910390f35b34156100a657600080fd5b6100ae610220565b6040516100bb9190610a7d565b60405180910390f35b34156100cf57600080fd5b6100d76102c5565b6040516100e49190610ae3565b60405180910390f35b34156100f857600080fd5b610100610350565b60405161010d9190610ac1565b60405180910390f35b341561012157600080fd5b610129610399565b6040516101369190610b05565b60405180910390f35b6060600260405190808252806020026020018201604052801561017657816020015b60608152602001906001900390816101615790505b5090506040805190810160405280600581526020017f48656c6c6f0000000000000000000000000000000000000000000000000000008152508160008151811015156101be57fe5b906020019060200201819052506040805190810160405280600581526020017f576f726c6400000000000000000000000000000000000000000000000000000081525081600181518110151561021057fe5b9060200190602002018190525090565b610228610546565b6040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081600060028110151561026d57fe5b60200201819052506040805190810160405280600581526020017f576f726c640000000000000000000000000000000000000000000000000000008152508160016002811015156102ba57fe5b602002018190525090565b6060600260405190808252806020026020018201604052801561030257816020015b6102ef61056d565b8152602001906001900390816102e75790505b50905061030d610399565b81600081518110151561031c57fe5b90602001906020020181905250610331610399565b81600181518110151561034057fe5b9060200190602002018190525090565b6103586105a9565b610360610399565b81600060028110151561036f57fe5b602002018190525061037f610399565b81600160028110151561038e57fe5b602002018190525090565b6103a16105d8565b60606103ab610614565b6103b3610546565b60036040519080825280602002602001820160405280156103e35781602001602082028038833980820191505090505b50925060008360008151811015156103f757fe5b9060200190602002018181525050600183600181518110151561041657fe5b9060200190602002018181525050600283600281518110151561043557fe5b90602001906020020181815250506040805190810160405280600081526020016001815250915060408051908101604052806040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081526020016040805190810160405280600581526020017f576f726c64000000000000000000000000000000000000000000000000000000815250815250905060a060405190810160405280600181526020016040805190810160405280600b81526020017f48656c6c6f20776f726c64000000000000000000000000000000000000000000815250815260200183815260200184815260200182815250935083935050505090565b60408051908101604052806002905b60608152602001906001900390816105555790505090565b60e060405190810160405280600081526020016060815260200161058f610636565b8152602001606081526020016105a3610658565b81525090565b6101c0604051908101604052806002905b6105c261056d565b8152602001906001900390816105ba5790505090565b60e06040519081016040528060008152602001606081526020016105fa610636565b81526020016060815260200161060e610658565b81525090565b6040805190810160405280600290602082028038833980820191505090505090565b6040805190810160405280600290602082028038833980820191505090505090565b60408051908101604052806002905b60608152602001906001900390816106675790505090565b600061068a82610b81565b8360208202850161069a85610b31565b60005b848110156106d35783830388526106b5838351610930565b92506106c082610bdb565b915060208801975060018101905061069d565b508196508694505050505092915050565b60006106ef82610b76565b836020820285016106ff85610b27565b60005b8481101561073857838303885261071a838351610930565b925061072582610bce565b9150602088019750600181019050610702565b508196508694505050505092915050565b600061075482610b8c565b8084526020840193508360208202850161076d85610b3b565b60005b848110156107a6578383038852610788838351610930565b925061079382610be8565b9150602088019750600181019050610770565b508196508694505050505092915050565b60006107c282610b97565b836020820285016107d285610b48565b60005b8481101561080b5783830388526107ed8383516109ea565b92506107f882610bf5565b91506020880197506001810190506107d5565b508196508694505050505092915050565b600061082782610ba2565b8084526020840193508360208202850161084085610b52565b60005b8481101561087957838303885261085b8383516109ea565b925061086682610c02565b9150602088019750600181019050610843565b508196508694505050505092915050565b61089381610bad565b61089c82610b5f565b60005b828110156108ce576108b2858351610a6e565b6108bb82610c0f565b915060208501945060018101905061089f565b5050505050565b60006108e082610bb8565b8084526020840193506108f283610b69565b60005b8281101561092457610908868351610a6e565b61091182610c1c565b91506020860195506001810190506108f5565b50849250505092915050565b600061093b82610bc3565b80845261094f816020860160208601610c33565b61095881610c66565b602085010191505092915050565b600060c08301600083015161097e6000860182610a6e565b50602083015184820360208601526109968282610930565b91505060408301516109ab604086018261088a565b50606083015184820360808601526109c382826108d5565b915050608083015184820360a08601526109dd82826106e4565b9150508091505092915050565b600060c083016000830151610a026000860182610a6e565b5060208301518482036020860152610a1a8282610930565b9150506040830151610a2f604086018261088a565b5060608301518482036080860152610a4782826108d5565b915050608083015184820360a0860152610a6182826106e4565b9150508091505092915050565b610a7781610c29565b82525050565b60006020820190508181036000830152610a97818461067f565b905092915050565b60006020820190508181036000830152610ab98184610749565b905092915050565b60006020820190508181036000830152610adb81846107b7565b905092915050565b60006020820190508181036000830152610afd818461081c565b905092915050565b60006020820190508181036000830152610b1f8184610966565b905092915050565b6000819050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b600060029050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000819050919050565b60005b83811015610c51578082015181840152602081019050610c36565b83811115610c60576000848401525b50505050565b6000601f19601f83011690509190505600a265627a7a72305820fdaf8ce6fe282a46498c8066d5b5ac382b69969eee38e1ad03c0d501b27f65366c6578706572696d656e74616cf50037")! let web3 = try await Web3.new(LocalTestCase.url) - let allAddresses = try await web3.eth.ownedAccounts() - var contract = web3.contract(abiString, at: nil, abiVersion: 2)! - - let parameters: [Any] = [] - // MARK: Writing Data flow - let deployTx = contract.prepareDeploy(bytecode: bytecode, parameters: parameters)! - deployTx.transaction.from = allAddresses[0] - let policies = Policies(gasLimitPolicy: .manual(3000000)) - let result = try await deployTx.writeToChain(password: "web3swift", policies: policies, sendRaw: false) - let txHash = result.hash.stripHexPrefix() - - Thread.sleep(forTimeInterval: 1.0) - - let receipt = try await web3.eth.transactionReceipt(Data.fromHex(txHash)!) + let receipt = try await deployContract(bytecode: bytecode, abiString: abiString) switch receipt.status { case .notYetProcessed: @@ -73,7 +47,7 @@ class AdvancedABIv2Tests: LocalTestCase { break } - contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! + let contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! // MARK: Read data from ABI flow // MARK: - Encoding ABI Data flow let tx = contract.createReadOperation("testStaticArray") @@ -85,20 +59,7 @@ class AdvancedABIv2Tests: LocalTestCase { let bytecode = Data.fromHex("6080604052341561000f57600080fd5b610cb18061001e6000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063189533931461007257806338163ff51461009b57806388be6c65146100c4578063e7c1a47c146100ed578063f376e01314610116575b600080fd5b341561007d57600080fd5b61008561013f565b6040516100929190610a9f565b60405180910390f35b34156100a657600080fd5b6100ae610220565b6040516100bb9190610a7d565b60405180910390f35b34156100cf57600080fd5b6100d76102c5565b6040516100e49190610ae3565b60405180910390f35b34156100f857600080fd5b610100610350565b60405161010d9190610ac1565b60405180910390f35b341561012157600080fd5b610129610399565b6040516101369190610b05565b60405180910390f35b6060600260405190808252806020026020018201604052801561017657816020015b60608152602001906001900390816101615790505b5090506040805190810160405280600581526020017f48656c6c6f0000000000000000000000000000000000000000000000000000008152508160008151811015156101be57fe5b906020019060200201819052506040805190810160405280600581526020017f576f726c6400000000000000000000000000000000000000000000000000000081525081600181518110151561021057fe5b9060200190602002018190525090565b610228610546565b6040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081600060028110151561026d57fe5b60200201819052506040805190810160405280600581526020017f576f726c640000000000000000000000000000000000000000000000000000008152508160016002811015156102ba57fe5b602002018190525090565b6060600260405190808252806020026020018201604052801561030257816020015b6102ef61056d565b8152602001906001900390816102e75790505b50905061030d610399565b81600081518110151561031c57fe5b90602001906020020181905250610331610399565b81600181518110151561034057fe5b9060200190602002018190525090565b6103586105a9565b610360610399565b81600060028110151561036f57fe5b602002018190525061037f610399565b81600160028110151561038e57fe5b602002018190525090565b6103a16105d8565b60606103ab610614565b6103b3610546565b60036040519080825280602002602001820160405280156103e35781602001602082028038833980820191505090505b50925060008360008151811015156103f757fe5b9060200190602002018181525050600183600181518110151561041657fe5b9060200190602002018181525050600283600281518110151561043557fe5b90602001906020020181815250506040805190810160405280600081526020016001815250915060408051908101604052806040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081526020016040805190810160405280600581526020017f576f726c64000000000000000000000000000000000000000000000000000000815250815250905060a060405190810160405280600181526020016040805190810160405280600b81526020017f48656c6c6f20776f726c64000000000000000000000000000000000000000000815250815260200183815260200184815260200182815250935083935050505090565b60408051908101604052806002905b60608152602001906001900390816105555790505090565b60e060405190810160405280600081526020016060815260200161058f610636565b8152602001606081526020016105a3610658565b81525090565b6101c0604051908101604052806002905b6105c261056d565b8152602001906001900390816105ba5790505090565b60e06040519081016040528060008152602001606081526020016105fa610636565b81526020016060815260200161060e610658565b81525090565b6040805190810160405280600290602082028038833980820191505090505090565b6040805190810160405280600290602082028038833980820191505090505090565b60408051908101604052806002905b60608152602001906001900390816106675790505090565b600061068a82610b81565b8360208202850161069a85610b31565b60005b848110156106d35783830388526106b5838351610930565b92506106c082610bdb565b915060208801975060018101905061069d565b508196508694505050505092915050565b60006106ef82610b76565b836020820285016106ff85610b27565b60005b8481101561073857838303885261071a838351610930565b925061072582610bce565b9150602088019750600181019050610702565b508196508694505050505092915050565b600061075482610b8c565b8084526020840193508360208202850161076d85610b3b565b60005b848110156107a6578383038852610788838351610930565b925061079382610be8565b9150602088019750600181019050610770565b508196508694505050505092915050565b60006107c282610b97565b836020820285016107d285610b48565b60005b8481101561080b5783830388526107ed8383516109ea565b92506107f882610bf5565b91506020880197506001810190506107d5565b508196508694505050505092915050565b600061082782610ba2565b8084526020840193508360208202850161084085610b52565b60005b8481101561087957838303885261085b8383516109ea565b925061086682610c02565b9150602088019750600181019050610843565b508196508694505050505092915050565b61089381610bad565b61089c82610b5f565b60005b828110156108ce576108b2858351610a6e565b6108bb82610c0f565b915060208501945060018101905061089f565b5050505050565b60006108e082610bb8565b8084526020840193506108f283610b69565b60005b8281101561092457610908868351610a6e565b61091182610c1c565b91506020860195506001810190506108f5565b50849250505092915050565b600061093b82610bc3565b80845261094f816020860160208601610c33565b61095881610c66565b602085010191505092915050565b600060c08301600083015161097e6000860182610a6e565b50602083015184820360208601526109968282610930565b91505060408301516109ab604086018261088a565b50606083015184820360808601526109c382826108d5565b915050608083015184820360a08601526109dd82826106e4565b9150508091505092915050565b600060c083016000830151610a026000860182610a6e565b5060208301518482036020860152610a1a8282610930565b9150506040830151610a2f604086018261088a565b5060608301518482036080860152610a4782826108d5565b915050608083015184820360a0860152610a6182826106e4565b9150508091505092915050565b610a7781610c29565b82525050565b60006020820190508181036000830152610a97818461067f565b905092915050565b60006020820190508181036000830152610ab98184610749565b905092915050565b60006020820190508181036000830152610adb81846107b7565b905092915050565b60006020820190508181036000830152610afd818461081c565b905092915050565b60006020820190508181036000830152610b1f8184610966565b905092915050565b6000819050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b600060029050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000819050919050565b60005b83811015610c51578082015181840152602081019050610c36565b83811115610c60576000848401525b50505050565b6000601f19601f83011690509190505600a265627a7a72305820fdaf8ce6fe282a46498c8066d5b5ac382b69969eee38e1ad03c0d501b27f65366c6578706572696d656e74616cf50037")! let web3 = try await Web3.new(LocalTestCase.url) - let allAddresses = try await web3.eth.ownedAccounts() - var contract = web3.contract(abiString, at: nil, abiVersion: 2)! - - let parameters: [Any] = [] - // MARK: Writing Data flow - let deployTx = contract.prepareDeploy(bytecode: bytecode, parameters: parameters)! - deployTx.transaction.from = allAddresses[0] - let policies = Policies(gasLimitPolicy: .manual(3000000)) - let result = try await deployTx.writeToChain(password: "web3swift", policies: policies, sendRaw: false) - let txHash = result.hash.stripHexPrefix() - - Thread.sleep(forTimeInterval: 1.0) - - let receipt = try await web3.eth.transactionReceipt(Data.fromHex(txHash)!) + let receipt = try await deployContract(bytecode: bytecode, abiString: abiString) switch receipt.status { case .notYetProcessed: @@ -107,7 +68,7 @@ class AdvancedABIv2Tests: LocalTestCase { break } - contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! + let contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! let tx = contract.createReadOperation("testDynArray") _ = try await tx!.callContractMethod() @@ -118,20 +79,7 @@ class AdvancedABIv2Tests: LocalTestCase { let bytecode = Data.fromHex("6080604052341561000f57600080fd5b610cb18061001e6000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063189533931461007257806338163ff51461009b57806388be6c65146100c4578063e7c1a47c146100ed578063f376e01314610116575b600080fd5b341561007d57600080fd5b61008561013f565b6040516100929190610a9f565b60405180910390f35b34156100a657600080fd5b6100ae610220565b6040516100bb9190610a7d565b60405180910390f35b34156100cf57600080fd5b6100d76102c5565b6040516100e49190610ae3565b60405180910390f35b34156100f857600080fd5b610100610350565b60405161010d9190610ac1565b60405180910390f35b341561012157600080fd5b610129610399565b6040516101369190610b05565b60405180910390f35b6060600260405190808252806020026020018201604052801561017657816020015b60608152602001906001900390816101615790505b5090506040805190810160405280600581526020017f48656c6c6f0000000000000000000000000000000000000000000000000000008152508160008151811015156101be57fe5b906020019060200201819052506040805190810160405280600581526020017f576f726c6400000000000000000000000000000000000000000000000000000081525081600181518110151561021057fe5b9060200190602002018190525090565b610228610546565b6040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081600060028110151561026d57fe5b60200201819052506040805190810160405280600581526020017f576f726c640000000000000000000000000000000000000000000000000000008152508160016002811015156102ba57fe5b602002018190525090565b6060600260405190808252806020026020018201604052801561030257816020015b6102ef61056d565b8152602001906001900390816102e75790505b50905061030d610399565b81600081518110151561031c57fe5b90602001906020020181905250610331610399565b81600181518110151561034057fe5b9060200190602002018190525090565b6103586105a9565b610360610399565b81600060028110151561036f57fe5b602002018190525061037f610399565b81600160028110151561038e57fe5b602002018190525090565b6103a16105d8565b60606103ab610614565b6103b3610546565b60036040519080825280602002602001820160405280156103e35781602001602082028038833980820191505090505b50925060008360008151811015156103f757fe5b9060200190602002018181525050600183600181518110151561041657fe5b9060200190602002018181525050600283600281518110151561043557fe5b90602001906020020181815250506040805190810160405280600081526020016001815250915060408051908101604052806040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081526020016040805190810160405280600581526020017f576f726c64000000000000000000000000000000000000000000000000000000815250815250905060a060405190810160405280600181526020016040805190810160405280600b81526020017f48656c6c6f20776f726c64000000000000000000000000000000000000000000815250815260200183815260200184815260200182815250935083935050505090565b60408051908101604052806002905b60608152602001906001900390816105555790505090565b60e060405190810160405280600081526020016060815260200161058f610636565b8152602001606081526020016105a3610658565b81525090565b6101c0604051908101604052806002905b6105c261056d565b8152602001906001900390816105ba5790505090565b60e06040519081016040528060008152602001606081526020016105fa610636565b81526020016060815260200161060e610658565b81525090565b6040805190810160405280600290602082028038833980820191505090505090565b6040805190810160405280600290602082028038833980820191505090505090565b60408051908101604052806002905b60608152602001906001900390816106675790505090565b600061068a82610b81565b8360208202850161069a85610b31565b60005b848110156106d35783830388526106b5838351610930565b92506106c082610bdb565b915060208801975060018101905061069d565b508196508694505050505092915050565b60006106ef82610b76565b836020820285016106ff85610b27565b60005b8481101561073857838303885261071a838351610930565b925061072582610bce565b9150602088019750600181019050610702565b508196508694505050505092915050565b600061075482610b8c565b8084526020840193508360208202850161076d85610b3b565b60005b848110156107a6578383038852610788838351610930565b925061079382610be8565b9150602088019750600181019050610770565b508196508694505050505092915050565b60006107c282610b97565b836020820285016107d285610b48565b60005b8481101561080b5783830388526107ed8383516109ea565b92506107f882610bf5565b91506020880197506001810190506107d5565b508196508694505050505092915050565b600061082782610ba2565b8084526020840193508360208202850161084085610b52565b60005b8481101561087957838303885261085b8383516109ea565b925061086682610c02565b9150602088019750600181019050610843565b508196508694505050505092915050565b61089381610bad565b61089c82610b5f565b60005b828110156108ce576108b2858351610a6e565b6108bb82610c0f565b915060208501945060018101905061089f565b5050505050565b60006108e082610bb8565b8084526020840193506108f283610b69565b60005b8281101561092457610908868351610a6e565b61091182610c1c565b91506020860195506001810190506108f5565b50849250505092915050565b600061093b82610bc3565b80845261094f816020860160208601610c33565b61095881610c66565b602085010191505092915050565b600060c08301600083015161097e6000860182610a6e565b50602083015184820360208601526109968282610930565b91505060408301516109ab604086018261088a565b50606083015184820360808601526109c382826108d5565b915050608083015184820360a08601526109dd82826106e4565b9150508091505092915050565b600060c083016000830151610a026000860182610a6e565b5060208301518482036020860152610a1a8282610930565b9150506040830151610a2f604086018261088a565b5060608301518482036080860152610a4782826108d5565b915050608083015184820360a0860152610a6182826106e4565b9150508091505092915050565b610a7781610c29565b82525050565b60006020820190508181036000830152610a97818461067f565b905092915050565b60006020820190508181036000830152610ab98184610749565b905092915050565b60006020820190508181036000830152610adb81846107b7565b905092915050565b60006020820190508181036000830152610afd818461081c565b905092915050565b60006020820190508181036000830152610b1f8184610966565b905092915050565b6000819050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b600060029050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000819050919050565b60005b83811015610c51578082015181840152602081019050610c36565b83811115610c60576000848401525b50505050565b6000601f19601f83011690509190505600a265627a7a72305820fdaf8ce6fe282a46498c8066d5b5ac382b69969eee38e1ad03c0d501b27f65366c6578706572696d656e74616cf50037")! let web3 = try await Web3.new(LocalTestCase.url) - let allAddresses = try await web3.eth.ownedAccounts() - var contract = web3.contract(abiString, at: nil, abiVersion: 2)! - - let parameters: [Any] = [] - // MARK: Writing Data flow - let deployTx = contract.prepareDeploy(bytecode: bytecode, parameters: parameters)! - deployTx.transaction.from = allAddresses[0] - let policies = Policies(gasLimitPolicy: .manual(3000000)) - let result = try await deployTx.writeToChain(password: "web3swift", policies: policies, sendRaw: false) - let txHash = result.hash.stripHexPrefix() - - Thread.sleep(forTimeInterval: 1.0) - - let receipt = try await web3.eth.transactionReceipt(Data.fromHex(txHash)!) + let receipt = try await deployContract(bytecode: bytecode, abiString: abiString) switch receipt.status { case .notYetProcessed: @@ -140,7 +88,7 @@ class AdvancedABIv2Tests: LocalTestCase { break } - contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! + let contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! // MARK: Read data from ABI flow // MARK: - Encoding ABI Data flow let tx = contract.createReadOperation("testDynOfDyn") @@ -152,20 +100,7 @@ class AdvancedABIv2Tests: LocalTestCase { let bytecode = Data.fromHex("6080604052341561000f57600080fd5b610cb18061001e6000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063189533931461007257806338163ff51461009b57806388be6c65146100c4578063e7c1a47c146100ed578063f376e01314610116575b600080fd5b341561007d57600080fd5b61008561013f565b6040516100929190610a9f565b60405180910390f35b34156100a657600080fd5b6100ae610220565b6040516100bb9190610a7d565b60405180910390f35b34156100cf57600080fd5b6100d76102c5565b6040516100e49190610ae3565b60405180910390f35b34156100f857600080fd5b610100610350565b60405161010d9190610ac1565b60405180910390f35b341561012157600080fd5b610129610399565b6040516101369190610b05565b60405180910390f35b6060600260405190808252806020026020018201604052801561017657816020015b60608152602001906001900390816101615790505b5090506040805190810160405280600581526020017f48656c6c6f0000000000000000000000000000000000000000000000000000008152508160008151811015156101be57fe5b906020019060200201819052506040805190810160405280600581526020017f576f726c6400000000000000000000000000000000000000000000000000000081525081600181518110151561021057fe5b9060200190602002018190525090565b610228610546565b6040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081600060028110151561026d57fe5b60200201819052506040805190810160405280600581526020017f576f726c640000000000000000000000000000000000000000000000000000008152508160016002811015156102ba57fe5b602002018190525090565b6060600260405190808252806020026020018201604052801561030257816020015b6102ef61056d565b8152602001906001900390816102e75790505b50905061030d610399565b81600081518110151561031c57fe5b90602001906020020181905250610331610399565b81600181518110151561034057fe5b9060200190602002018190525090565b6103586105a9565b610360610399565b81600060028110151561036f57fe5b602002018190525061037f610399565b81600160028110151561038e57fe5b602002018190525090565b6103a16105d8565b60606103ab610614565b6103b3610546565b60036040519080825280602002602001820160405280156103e35781602001602082028038833980820191505090505b50925060008360008151811015156103f757fe5b9060200190602002018181525050600183600181518110151561041657fe5b9060200190602002018181525050600283600281518110151561043557fe5b90602001906020020181815250506040805190810160405280600081526020016001815250915060408051908101604052806040805190810160405280600581526020017f48656c6c6f00000000000000000000000000000000000000000000000000000081525081526020016040805190810160405280600581526020017f576f726c64000000000000000000000000000000000000000000000000000000815250815250905060a060405190810160405280600181526020016040805190810160405280600b81526020017f48656c6c6f20776f726c64000000000000000000000000000000000000000000815250815260200183815260200184815260200182815250935083935050505090565b60408051908101604052806002905b60608152602001906001900390816105555790505090565b60e060405190810160405280600081526020016060815260200161058f610636565b8152602001606081526020016105a3610658565b81525090565b6101c0604051908101604052806002905b6105c261056d565b8152602001906001900390816105ba5790505090565b60e06040519081016040528060008152602001606081526020016105fa610636565b81526020016060815260200161060e610658565b81525090565b6040805190810160405280600290602082028038833980820191505090505090565b6040805190810160405280600290602082028038833980820191505090505090565b60408051908101604052806002905b60608152602001906001900390816106675790505090565b600061068a82610b81565b8360208202850161069a85610b31565b60005b848110156106d35783830388526106b5838351610930565b92506106c082610bdb565b915060208801975060018101905061069d565b508196508694505050505092915050565b60006106ef82610b76565b836020820285016106ff85610b27565b60005b8481101561073857838303885261071a838351610930565b925061072582610bce565b9150602088019750600181019050610702565b508196508694505050505092915050565b600061075482610b8c565b8084526020840193508360208202850161076d85610b3b565b60005b848110156107a6578383038852610788838351610930565b925061079382610be8565b9150602088019750600181019050610770565b508196508694505050505092915050565b60006107c282610b97565b836020820285016107d285610b48565b60005b8481101561080b5783830388526107ed8383516109ea565b92506107f882610bf5565b91506020880197506001810190506107d5565b508196508694505050505092915050565b600061082782610ba2565b8084526020840193508360208202850161084085610b52565b60005b8481101561087957838303885261085b8383516109ea565b925061086682610c02565b9150602088019750600181019050610843565b508196508694505050505092915050565b61089381610bad565b61089c82610b5f565b60005b828110156108ce576108b2858351610a6e565b6108bb82610c0f565b915060208501945060018101905061089f565b5050505050565b60006108e082610bb8565b8084526020840193506108f283610b69565b60005b8281101561092457610908868351610a6e565b61091182610c1c565b91506020860195506001810190506108f5565b50849250505092915050565b600061093b82610bc3565b80845261094f816020860160208601610c33565b61095881610c66565b602085010191505092915050565b600060c08301600083015161097e6000860182610a6e565b50602083015184820360208601526109968282610930565b91505060408301516109ab604086018261088a565b50606083015184820360808601526109c382826108d5565b915050608083015184820360a08601526109dd82826106e4565b9150508091505092915050565b600060c083016000830151610a026000860182610a6e565b5060208301518482036020860152610a1a8282610930565b9150506040830151610a2f604086018261088a565b5060608301518482036080860152610a4782826108d5565b915050608083015184820360a0860152610a6182826106e4565b9150508091505092915050565b610a7781610c29565b82525050565b60006020820190508181036000830152610a97818461067f565b905092915050565b60006020820190508181036000830152610ab98184610749565b905092915050565b60006020820190508181036000830152610adb81846107b7565b905092915050565b60006020820190508181036000830152610afd818461081c565b905092915050565b60006020820190508181036000830152610b1f8184610966565b905092915050565b6000819050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b6000819050919050565b6000602082019050919050565b600060029050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000819050919050565b60005b83811015610c51578082015181840152602081019050610c36565b83811115610c60576000848401525b50505050565b6000601f19601f83011690509190505600a265627a7a72305820fdaf8ce6fe282a46498c8066d5b5ac382b69969eee38e1ad03c0d501b27f65366c6578706572696d656e74616cf50037")! let web3 = try await Web3.new(LocalTestCase.url) - let allAddresses = try await web3.eth.ownedAccounts() - var contract = web3.contract(abiString, at: nil, abiVersion: 2)! - - let parameters: [Any] = [] - // MARK: Writing Data flow - let deployTx = contract.prepareDeploy(bytecode: bytecode, parameters: parameters)! - deployTx.transaction.from = allAddresses[0] - let policies = Policies(gasLimitPolicy: .manual(3000000)) - let result = try await deployTx.writeToChain(password: "web3swift", policies: policies, sendRaw: false) - let txHash = result.hash.stripHexPrefix() - - Thread.sleep(forTimeInterval: 1.0) - - let receipt = try await web3.eth.transactionReceipt(Data.fromHex(txHash)!) + let receipt = try await deployContract(bytecode: bytecode, abiString: abiString) switch receipt.status { case .notYetProcessed: @@ -174,7 +109,7 @@ class AdvancedABIv2Tests: LocalTestCase { break } - contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! + let contract = web3.contract(abiString, at: receipt.contractAddress, abiVersion: 2)! // MARK: Read data from ABI flow // MARK: - Encoding ABI Data flow let tx = contract.createReadOperation("testStOfDyn") @@ -182,8 +117,10 @@ class AdvancedABIv2Tests: LocalTestCase { } func testEmptyArrayDecoding() async throws { + let bytecode = Data(hex: "608060405234801561001057600080fd5b5061027b806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f2a75fe414610030575b600080fd5b61003861004e565b60405161004591906101e0565b60405180910390f35b60606000600267ffffffffffffffff811115610093577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280602002602001820160405280156100c15781602001602082028036833780820191505090505b509050600181600081518110610100577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602002602001018181525050600281600181518110610148577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020026020010181815250508091505090565b600061016783836101d1565b60208301905092915050565b600061017e82610212565b610188818561022a565b935061019383610202565b8060005b838110156101c45781516101ab888261015b565b97506101b68361021d565b925050600181019050610197565b5085935050505092915050565b6101da8161023b565b82525050565b600060208201905081810360008301526101fa8184610173565b905092915050565b6000819050602082019050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b600081905091905056fea2646970667358221220d1b52dfe3238df01604ccfbb6c6cee01edbaa74fcffd8a57c4041b0b19e6887664736f6c63430008040033") let abiString = "[{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"constant\":true,\"inputs\":[],\"name\":\"empty\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]" - let contractAddress = EthereumAddress("0x200eb5ccda1c35b0f5bf82552fdd65a8aee98e79")! + let receipt = try await deployContract(bytecode: bytecode, abiString: abiString) + let contractAddress = receipt.contractAddress! let web3 = try await Web3.new(LocalTestCase.url) let contract = web3.contract(abiString, at: contractAddress, abiVersion: 2) XCTAssert(contract != nil) @@ -191,20 +128,31 @@ class AdvancedABIv2Tests: LocalTestCase { // MARK: - Encoding ABI Data flow let tx = contract?.createReadOperation("empty") XCTAssertNotNil(tx) - _ = try await tx!.callContractMethod() + let result = try await tx!.callContractMethod() + XCTAssertEqual(result.count, 1) + XCTAssertEqual((result["0"] as? Array)?[0], 1) + XCTAssertEqual((result["0"] as? Array)?[1], 2) } func testUserCase() async throws { + let bytecode = Data(hex: "0x608060405234801561001057600080fd5b50610484806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a16e94bf1461003b578063a46b5b6b14610059575b600080fd5b610043610075565b60405161005091906102a6565b60405180910390f35b610073600480360381019061006e919061022c565b610107565b005b6060600080546100849061037c565b80601f01602080910402602001604051908101604052809291908181526020018280546100b09061037c565b80156100fd5780601f106100d2576101008083540402835291602001916100fd565b820191906000526020600020905b8154815290600101906020018083116100e057829003601f168201915b5050505050905090565b806000908051906020019061011d929190610121565b5050565b82805461012d9061037c565b90600052602060002090601f01602090048101928261014f5760008555610196565b82601f1061016857805160ff1916838001178555610196565b82800160010185558215610196579182015b8281111561019557825182559160200191906001019061017a565b5b5090506101a391906101a7565b5090565b5b808211156101c05760008160009055506001016101a8565b5090565b60006101d76101d2846102ed565b6102c8565b9050828152602081018484840111156101ef57600080fd5b6101fa84828561033a565b509392505050565b600082601f83011261021357600080fd5b81356102238482602086016101c4565b91505092915050565b60006020828403121561023e57600080fd5b600082013567ffffffffffffffff81111561025857600080fd5b61026484828501610202565b91505092915050565b60006102788261031e565b6102828185610329565b9350610292818560208601610349565b61029b8161043d565b840191505092915050565b600060208201905081810360008301526102c0818461026d565b905092915050565b60006102d26102e3565b90506102de82826103ae565b919050565b6000604051905090565b600067ffffffffffffffff8211156103085761030761040e565b5b6103118261043d565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b82818337600083830152505050565b60005b8381101561036757808201518184015260208101905061034c565b83811115610376576000848401525b50505050565b6000600282049050600182168061039457607f821691505b602082108114156103a8576103a76103df565b5b50919050565b6103b78261043d565b810181811067ffffffffffffffff821117156103d6576103d561040e565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f830116905091905056fea2646970667358221220264496be069122017e0e6bd1c3b06e335df83ac7d058c0063a81e9227786693564736f6c63430008040033") let abiString = "[{\"constant\":true,\"inputs\":[],\"name\":\"getFlagData\",\"outputs\":[{\"name\":\"data\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"data\",\"type\":\"string\"}],\"name\":\"setFlagData\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" - let contractAddress = EthereumAddress("0x811411e3cdfd4750cdd3552feb3b89a46ddb612e") - let web3 = try await Web3.new(LocalTestCase.url) + let receipt = try await deployContract(bytecode: bytecode, abiString: abiString) + let contractAddress = receipt.contractAddress! + let web3 = try await Web3(provider: Web3HttpProvider(url: LocalTestCase.url, network: nil)) let contract = web3.contract(abiString, at: contractAddress, abiVersion: 2) XCTAssert(contract != nil) + let allAddresses = try await web3.eth.ownedAccounts() + let balance = try await web3.eth.getBalance(for: allAddresses[0]) + let writeOperation = contract?.createWriteOperation("setFlagData", parameters: ["abcdefg"]) + writeOperation?.transaction.from = allAddresses[0] + try await writeOperation?.writeToChain(password: "web3swift", policies: Policies(gasLimitPolicy: .manual(3000000)), sendRaw: false) // MARK: Read data from ABI flow // MARK: - Encoding ABI Data flow let tx = contract?.createReadOperation("getFlagData") XCTAssertNotNil(tx) - _ = try await tx!.callContractMethod() + let result = try await tx!.callContractMethod() + XCTAssertEqual(result["0"] as? String, "abcdefg") } } diff --git a/Tests/web3swiftTests/localTests/LocalTestCase.swift b/Tests/web3swiftTests/localTests/LocalTestCase.swift index 7b79589c4..707fbde93 100644 --- a/Tests/web3swiftTests/localTests/LocalTestCase.swift +++ b/Tests/web3swiftTests/localTests/LocalTestCase.swift @@ -31,4 +31,21 @@ class LocalTestCase: XCTestCase { _ = try! await writeTX.writeToChain(password: "", policies: policies, sendRaw: false) } } + + func deployContract(bytecode: Data, abiString: String) async throws -> TransactionReceipt { + let web3 = try await Web3.new(LocalTestCase.url) + let allAddresses = try await web3.eth.ownedAccounts() + var contract = web3.contract(abiString, at: nil, abiVersion: 2)! + + let parameters: [Any] = [] + // MARK: Writing Data flow + let deployTx = contract.prepareDeploy(bytecode: bytecode, parameters: parameters)! + deployTx.transaction.from = allAddresses[0] + let policies = Policies(gasLimitPolicy: .manual(3000000)) + let result = try await deployTx.writeToChain(password: "web3swift", policies: policies, sendRaw: false) + let txHash = result.hash.stripHexPrefix() + Thread.sleep(forTimeInterval: 1.0) + let receipt = try await web3.eth.transactionReceipt(Data.fromHex(txHash)!) + return receipt + } } diff --git a/Tests/web3swiftTests/localTests/String+ExtensionTests.swift.swift b/Tests/web3swiftTests/localTests/String+ExtensionTests.swift.swift new file mode 100644 index 000000000..1a6686178 --- /dev/null +++ b/Tests/web3swiftTests/localTests/String+ExtensionTests.swift.swift @@ -0,0 +1,31 @@ +// +// String+ExtensionTests.swift +// +// Created by JeneaVranceanu on 26.11.2023. +// + +import Foundation +import XCTest + +class StringExtensionsTest: XCTestCase { + + func testIsHex() throws { + XCTAssertTrue("0x".isHex) + XCTAssertTrue("0xF".isHex) + XCTAssertTrue("F".isHex) + XCTAssertTrue("0xFF".isHex) + XCTAssertTrue("0x0123456789abcdefABCDEF".isHex) + XCTAssertTrue("0123456789abcdefABCDEF".isHex) + XCTAssertTrue("0123456789abcdefABCDEF ".isHex) + XCTAssertTrue(" 0123456789abcdefABCDEF ".isHex) + XCTAssertTrue(" 0123456789abcdefABCDEF".isHex) + + XCTAssertFalse("".isHex) + XCTAssertFalse("-".isHex) + XCTAssertFalse("xyz".isHex) + XCTAssertFalse("0xCAFEQ".isHex) + XCTAssertFalse("R0123456789abcdefABCDEF ".isHex) + XCTAssertFalse(" R0123456789abcdefABCDEF ".isHex) + } + +} diff --git a/Tests/web3swiftTests/localTests/UncategorizedTests.swift b/Tests/web3swiftTests/localTests/UncategorizedTests.swift index 82917d4dc..fa6bcf4d7 100755 --- a/Tests/web3swiftTests/localTests/UncategorizedTests.swift +++ b/Tests/web3swiftTests/localTests/UncategorizedTests.swift @@ -10,7 +10,7 @@ import BigInt @testable import Web3Core @testable import web3swift -class UncategorizedTests: XCTestCase { +class UncategorizedTests: LocalTestCase { func testBitFunctions () throws { let data = Data([0xf0, 0x02, 0x03]) let firstBit = data.bitsInRange(0, 1) @@ -120,24 +120,28 @@ class UncategorizedTests: XCTestCase { // } func testPublicMappingsAccess() async throws { + let bytecode = Data(hex: "0x608060405234801561001057600080fd5b5061023d806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063365b98b2146100465780635e79ab6014610076578063bff1f9e1146100a6575b600080fd5b610060600480360381019061005b919061014a565b6100c4565b60405161006d9190610182565b60405180910390f35b610090600480360381019061008b9190610121565b6100ce565b60405161009d9190610182565b60405180910390f35b6100ae6100ee565b6040516100bb9190610182565b60405180910390f35b6000819050919050565b60008173ffffffffffffffffffffffffffffffffffffffff169050919050565b60006064905090565b600081359050610106816101d9565b92915050565b60008135905061011b816101f0565b92915050565b60006020828403121561013357600080fd5b6000610141848285016100f7565b91505092915050565b60006020828403121561015c57600080fd5b600061016a8482850161010c565b91505092915050565b61017c816101cf565b82525050565b60006020820190506101976000830184610173565b92915050565b60006101a8826101af565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6101e28161019d565b81146101ed57600080fd5b50565b6101f9816101cf565b811461020457600080fd5b5056fea26469706673582212207373b0db986284793522a82bff7bf03e30323defa94e6d25f7141e7d63e1ee0564736f6c63430008040033") let jsonString = "[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"users\",\"outputs\":[{\"name\":\"name\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"userDeviceCount\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalUsers\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]" + let receipt = try await deployContract(bytecode: bytecode, abiString: jsonString) let web3 = try await Web3.new(LocalTestCase.url) - guard let addr = EthereumAddress("0xdef61132a0c1259464b19e4590e33666aae38574") else {return XCTFail()} - let contract = web3.contract(jsonString, at: addr, abiVersion: 2) - XCTAssert(contract != nil) - let allMethods = contract!.contract.allMethods + guard let addr = receipt.contractAddress else {return XCTFail()} + let contract = web3.contract(jsonString, at: receipt.contractAddress!, abiVersion: 2) + let userDeviceCount = try await contract! - .createReadOperation("userDeviceCount", parameters: [addr])? + .createReadOperation("userDeviceCount", parameters: [addr])! .callContractMethod() let totalUsers = try await contract! - .createReadOperation("totalUsers")? + .createReadOperation("totalUsers")! .callContractMethod() let user = try await contract! - .createReadOperation("users", parameters: [0])? + .createReadOperation("users", parameters: [0])! .callContractMethod() + XCTAssertEqual((userDeviceCount["0"] as? BigUInt)?.hexString.lowercased(), addr.address.lowercased()) + XCTAssertEqual(totalUsers["0"] as? BigUInt, 100) + XCTAssertEqual(user["0"] as? BigUInt, 0) } func testBloomFilterPerformance() throws { diff --git a/Tests/web3swiftTests/localTests/UtilitiesTests.swift b/Tests/web3swiftTests/localTests/UtilitiesTests.swift index 4780805b0..057d99be2 100644 --- a/Tests/web3swiftTests/localTests/UtilitiesTests.swift +++ b/Tests/web3swiftTests/localTests/UtilitiesTests.swift @@ -82,4 +82,9 @@ class UtilitiesTests: XCTestCase { address = Utilities.publicToAddress(Data.fromHex("0x0852972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2")!)?.address XCTAssertEqual(address, nil) } + + func testStringIsHex() { + XCTAssertTrue("1234567890abcdef".isHex) + XCTAssertFalse("ghijklmn".isHex) + } } diff --git a/Tests/web3swiftTests/remoteTests/DecodeRemoteErrorTests.swift b/Tests/web3swiftTests/remoteTests/DecodeRemoteErrorTests.swift new file mode 100644 index 000000000..ed5a48bf2 --- /dev/null +++ b/Tests/web3swiftTests/remoteTests/DecodeRemoteErrorTests.swift @@ -0,0 +1,53 @@ +// +// DecodeRemoteErrorTests.swift +// +// Created by liugang zhang on 2023/8/25. +// + +import XCTest +import Web3Core + +@testable import web3swift + +final class DecodeRemoteErrorTests: XCTestCase { + + let entryPoint = EthereumAddress("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789")! + let factory = EthereumAddress("0x9406Cc6185a346906296840746125a0E44976454")! + let address = EthereumAddress("0x581074D2d9e50913eB37665b07CAFa9bFFdd1640")! + + func testDecodeRemoteFunc() async throws { + let web3 = try await Web3.InfuraMainnetWeb3(accessToken: Constants.infuraToken) + + let entryABI = try EthereumContract(abi: [ + .error(.init(name: "SenderAddressResult", + inputs: [.init(name: "sender", type: .address)])), + .function(.init(name: "getSenderAddress", + inputs: [.init(name: "initCode", type: .dynamicBytes)], + outputs: [], + constant: false, + payable: false)) + ], at: entryPoint) + + let factoryABI = try EthereumContract(abi: [ + .function(.init(name: "createAccount", + inputs: [ + .init(name: "owner", type: .address), + .init(name: "salt", type: .uint(bits: 256)) + ], + outputs: [], + constant: false, + payable: false)) + ]) + + let initCode = factory.addressData + factoryABI.method("createAccount", parameters: [address, 0], extraData: nil)! + + do { + try await entryABI.callStatic("getSenderAddress", parameters: [initCode], provider: web3.provider) + XCTFail() + } catch Web3Error.revertCustom(let signature, let args) { + XCTAssertEqual(signature, "SenderAddressResult(address)") + XCTAssertEqual((args["sender"] as? EthereumAddress)?.address, "0x9CF91286f22a1b799770fB5De0E66f3C4cc165d1") + XCTAssertEqual((args["0"] as? EthereumAddress)?.address, "0x9CF91286f22a1b799770fB5De0E66f3C4cc165d1") + } + } +} diff --git a/Tests/web3swiftTests/remoteTests/InfuraTests.swift b/Tests/web3swiftTests/remoteTests/InfuraTests.swift index fec9289ef..da1cdf412 100755 --- a/Tests/web3swiftTests/remoteTests/InfuraTests.swift +++ b/Tests/web3swiftTests/remoteTests/InfuraTests.swift @@ -10,7 +10,6 @@ import Web3Core // MARK: Works only with network connection class InfuraTests: XCTestCase { - func testGetBalance() async throws { let web3 = try await Web3.InfuraMainnetWeb3(accessToken: Constants.infuraToken) let address = EthereumAddress("0xd61b5ca425F8C8775882d4defefC68A6979DBbce")! diff --git a/Tests/web3swiftTests/remoteTests/Web3HttpProviderTests.swift b/Tests/web3swiftTests/remoteTests/Web3HttpProviderTests.swift new file mode 100644 index 000000000..b2a5e144b --- /dev/null +++ b/Tests/web3swiftTests/remoteTests/Web3HttpProviderTests.swift @@ -0,0 +1,33 @@ +// +// Web3HttpProviderTests.swift +// +// +// Created by liugang zhang on 2023/9/2. +// + +import XCTest +import Web3Core + +@testable import web3swift + +final class Web3HttpProviderTests: XCTestCase { + + /// if one of these rpc server lose efficacy, find a substitution from https://chainlist.org/ + func testGetNetwork() async throws { + let requestURLstring = "https://" + Networks.Mainnet.name + Constants.infuraHttpScheme + Constants.infuraToken + var web3 = try await Web3HttpProvider(url: URL(string: requestURLstring)!, network: nil) + XCTAssertEqual(web3.network?.chainID, 1) + + web3 = try await Web3HttpProvider(url: URL(string: "https://arbitrum-one.publicnode.com")!, network: nil) + XCTAssertEqual(web3.network?.chainID, 42161) + + web3 = try await Web3HttpProvider(url: URL(string: "https://rpc.ankr.com/bsc")!, network: nil) + XCTAssertEqual(web3.network?.chainID, 56) + + web3 = try await Web3HttpProvider(url: URL(string: "https://rpc-mainnet.maticvigil.com/")!, network: nil) + XCTAssertEqual(web3.network?.chainID, 137) + + web3 = try await Web3HttpProvider(url: URL(string: "https://optimism.gateway.tenderly.co")!, network: nil) + XCTAssertEqual(web3.network?.chainID, 10) + } +}