diff --git a/Example/web3swiftExample/web3swiftExample.xcodeproj/project.pbxproj b/Example/web3swiftExample/web3swiftExample.xcodeproj/project.pbxproj index 92bd60e..c78bd62 100644 --- a/Example/web3swiftExample/web3swiftExample.xcodeproj/project.pbxproj +++ b/Example/web3swiftExample/web3swiftExample.xcodeproj/project.pbxproj @@ -357,8 +357,9 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 62V9CKQN89; + DEVELOPMENT_TEAM = 5UH69GW6GE; INFOPLIST_FILE = web3swiftExample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.bankexfoundation.web3swiftExample; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -373,8 +374,9 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 62V9CKQN89; + DEVELOPMENT_TEAM = 5UH69GW6GE; INFOPLIST_FILE = web3swiftExample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.bankexfoundation.web3swiftExample; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/web3swift/ABI/Classes/RLP.swift b/web3swift/ABI/Classes/RLP.swift index 3787d25..4e5c3d8 100644 --- a/web3swift/ABI/Classes/RLP.swift +++ b/web3swift/ABI/Classes/RLP.swift @@ -146,6 +146,182 @@ struct RLP { } return encodedLength } + + static func decode(_ raw: String) -> RLPItem? { + guard let rawData = Data.fromHex(raw) else {return nil} + return decode(rawData) + } + + static func decode(_ raw: Data) -> RLPItem? { + if raw.count == 0 { + return RLPItem.noItem + } + var outputArray = [RLPItem]() + var bytesToParse = raw + while bytesToParse.count != 0 { + let (of, dl, t) = decodeLength(bytesToParse) + guard let offset = of, let dataLength = dl, let type = t else {return nil} + switch type { + case .empty: + break + case .data: + guard let slice = try? slice(data: bytesToParse, offset: offset, length: dataLength) else {return nil} + let data = Data(slice) + let rlpItem = RLPItem.init(content: .data(data)) + outputArray.append(rlpItem) + case .list: + guard let slice = try? slice(data: bytesToParse, offset: offset, length: dataLength) else {return nil} + guard let inside = decode(Data(slice)) else {return nil} + switch inside.content { + case .data(_): + return nil + default: + outputArray.append(inside) + } + } + guard let tail = try? slice(data: bytesToParse, start: offset + dataLength) else {return nil} + bytesToParse = tail + } + return RLPItem.init(content: .list(outputArray, 0)) + } + + enum UnderlyingType { + case empty + case data + case list + } + + struct RLPItem { + + enum RLPContent { + case noItem + case data(Data) + indirect case list([RLPItem], Int) + } + + var content: RLPContent + + var isData: Bool { + switch self.content { + case .noItem: + return false + case .data(_): + return true + case .list(_): + return false + } + } + + var isList: Bool { + switch self.content { + case .noItem: + return false + case .data(_): + return false + case .list(_): + return true + } + } + var count: Int? { + switch self.content { + case .noItem: + return nil + case .data(_): + return nil + case .list(let list, _): + return list.count + } + } + var hasNext: Bool { + switch self.content { + case .noItem: + return false + case .data(_): + return false + case .list(let list, let counter): + return list.count > counter + } + } + + subscript(index: Int) -> RLPItem? { + get { + guard self.hasNext else {return nil} + guard case .list(let list, _) = self.content else {return nil} + let item = list[index] + return item + } + } + + var data: Data? { + return self.getData() + } + + func getData() -> Data? { + if self.isList { + return nil + } + guard case .data(let data) = self.content else {return nil} + return data + } + + static var noItem: RLPItem { + return RLPItem.init(content: .noItem) + } + } + + internal static func decodeLength(_ input: Data) -> (offset: BigUInt?, length: BigUInt?, type: UnderlyingType?) { + do { + let length = BigUInt(input.count) + if (length == BigUInt(0)) { + return (0, 0, .empty) + } + let prefixByte = input[0] + if prefixByte <= 0x7f { + return (BigUInt(0), BigUInt(1), .data) + }else if prefixByte <= 0xb7 && length > BigUInt(prefixByte - 0x80) { + let dataLength = BigUInt(prefixByte - 0x80) + return (BigUInt(1), dataLength, .data) + } else if try prefixByte <= 0xbf && length > BigUInt(prefixByte - 0xb7) && length > BigUInt(prefixByte - 0xb7) + toBigUInt(slice(data: input, offset: BigUInt(1), length: BigUInt(prefixByte - 0xb7))) { + let lengthOfLength = BigUInt(prefixByte - 0xb7) + let dataLength = try toBigUInt(slice(data: input, offset: BigUInt(1), length: BigUInt(prefixByte - 0xb7))) + return (1 + lengthOfLength, dataLength, .data) + } else if prefixByte <= 0xf7 && length > BigUInt(prefixByte - 0xc0) { + let listLen = BigUInt(prefixByte - 0xc0) + return (1, listLen, .list) + } else if try prefixByte <= 0xff && length > BigUInt(prefixByte - 0xf7) && length > BigUInt(prefixByte - 0xf7) + toBigUInt(slice(data: input, offset: BigUInt(1), length: BigUInt(prefixByte - 0xf7))) { + let lengthOfListLength = BigUInt(prefixByte - 0xf7) + let listLength = try toBigUInt(slice(data: input, offset: BigUInt(1), length: BigUInt(prefixByte - 0xf7))) + return (1 + lengthOfListLength, listLength, .list) + } else { + return (nil, nil, nil) + } + } catch { + return (nil, nil, nil) + } + } + + internal static func slice(data: Data, offset: BigUInt, length: BigUInt) throws -> Data { + if BigUInt(data.count) < offset + length {throw Web3Error.dataError} + let slice = data[UInt64(offset) ..< UInt64(offset + length)] + return Data(slice) + } + + internal static func slice(data: Data, start: BigUInt) throws -> Data { + if BigUInt(data.count) < start {throw Web3Error.dataError} + let slice = data[UInt64(start) ..< UInt64(data.count)] + return Data(slice) + } + + internal static func toBigUInt(_ raw: Data) throws -> BigUInt { + if raw.count == 0 { + throw Web3Error.dataError + } else if raw.count == 1 { + return BigUInt.init(raw) + } else { + let slice = raw[0 ..< raw.count - 1] + return try BigUInt(raw[raw.count-1]) + toBigUInt(slice)*256 + } + } } diff --git a/web3swift/Concurrency/Classes/Web3+TransactionOperations.swift b/web3swift/Concurrency/Classes/Web3+TransactionOperations.swift index e9f4ad9..6fd57ae 100644 --- a/web3swift/Concurrency/Classes/Web3+TransactionOperations.swift +++ b/web3swift/Concurrency/Classes/Web3+TransactionOperations.swift @@ -168,6 +168,7 @@ final class ContractSendOperation: Web3Operation { } transaction.nonce = nonce intermediate.transaction = transaction + intermediate.options = options guard let gasEstimateOperation = ContractEstimateGasOperation.init(self.web3, queue: self.expectedQueue, intermediate: intermediate, onBlock: onBlock) else {return self.processError(Web3Error.dataError)} gasEstimateOperation.next = OperationChainingType.callback(gasEstimationCallback, self.expectedQueue) self.expectedQueue.addOperation(gasEstimateOperation) diff --git a/web3swift/Contract/Classes/Contract.swift b/web3swift/Contract/Classes/Contract.swift index 885a7b7..2f86e72 100644 --- a/web3swift/Contract/Classes/Contract.swift +++ b/web3swift/Contract/Classes/Contract.swift @@ -52,6 +52,10 @@ public struct Contract:ContractProtocol { continue } } + if toReturn == nil { + let defaultConstructor = ABIElement.constructor(ABIElement.Constructor.init(inputs: [], constant: false, payable: false)) + return defaultConstructor + } return toReturn } @@ -99,15 +103,8 @@ public struct Contract:ContractProtocol { } public func deploy(bytecode:Data, parameters: [AnyObject] = [AnyObject](), extraData: Data = Data(), options: Web3Options?) -> EthereumTransaction? { - var to:EthereumAddress + let to:EthereumAddress = EthereumAddress.contractDeploymentAddress() let mergedOptions = Web3Options.merge(self.options, with: options) - if (self.address != nil) { - to = self.address! - } else if let toFound = mergedOptions?.to, toFound.isValid { - to = toFound - } else { - return nil - } var gasLimit:BigUInt if let gasInOptions = mergedOptions?.gasLimit { diff --git a/web3swift/Contract/Classes/ContractABIv2.swift b/web3swift/Contract/Classes/ContractABIv2.swift index effef5b..aadfb75 100644 --- a/web3swift/Contract/Classes/ContractABIv2.swift +++ b/web3swift/Contract/Classes/ContractABIv2.swift @@ -57,6 +57,10 @@ public struct ContractV2:ContractProtocol { continue } } + if toReturn == nil { + let defaultConstructor = ABIv2.Element.constructor(ABIv2.Element.Constructor.init(inputs: [], constant: false, payable: false)) + return defaultConstructor + } return toReturn } @@ -104,16 +108,8 @@ public struct ContractV2:ContractProtocol { } public func deploy(bytecode:Data, parameters: [AnyObject] = [AnyObject](), extraData: Data = Data(), options: Web3Options?) -> EthereumTransaction? { - var to:EthereumAddress + let to:EthereumAddress = EthereumAddress.contractDeploymentAddress() let mergedOptions = Web3Options.merge(self.options, with: options) - if (self.address != nil) { - to = self.address! - } else if let toFound = mergedOptions?.to, toFound.isValid { - to = toFound - } else { - return nil - } - var gasLimit:BigUInt if let gasInOptions = mergedOptions?.gasLimit { gasLimit = gasInOptions diff --git a/web3swift/KeystoreManager/Classes/EthereumAddress.swift b/web3swift/KeystoreManager/Classes/EthereumAddress.swift index 88a541a..919e2a2 100644 --- a/web3swift/KeystoreManager/Classes/EthereumAddress.swift +++ b/web3swift/KeystoreManager/Classes/EthereumAddress.swift @@ -9,30 +9,46 @@ import Foundation import BigInt public struct EthereumAddress: Equatable { + public enum AddressType { + case normal + case contractDeployment + } + public var isValid: Bool { get { - return (self.addressData.count == 20); + switch self.type { + case .normal: + return (self.addressData.count == 20) + case .contractDeployment: + return true + } + } } var _address: String - + public var type: AddressType = .normal public static func ==(lhs: EthereumAddress, rhs: EthereumAddress) -> Bool { - return lhs.address.lowercased() == rhs.address.lowercased() + return lhs.address.lowercased() == rhs.address.lowercased() && lhs.type == rhs.type } public var addressData: Data { get { - let dataArray = Array(hex: _address.lowercased().stripHexPrefix()) - guard let d = Data(dataArray).setLengthLeft(20) - else { - return Data() + switch self.type { + case .normal: + guard let dataArray = Data.fromHex(_address) else {return Data()} + guard let d = dataArray.setLengthLeft(20) else { return Data()} + return d + case .contractDeployment: + return Data() } - return d } } public var address:String { - get { + switch self.type { + case .normal: return EthereumAddress.toChecksumAddress(_address)! + case .contractDeployment: + return "0x" } } @@ -56,11 +72,17 @@ public struct EthereumAddress: Equatable { return ret } - public init(_ addressString:String) { - _address = addressString + public init(_ addressString:String, type: AddressType = .normal) { + self._address = addressString + self.type = type + } + + public init(_ addressData:Data, type: AddressType = .normal) { + self._address = addressData.toHexString().addHexPrefix() + self.type = type } - public init(_ addressData:Data) { - _address = addressData.toHexString().addHexPrefix() + public static func contractDeploymentAddress() -> EthereumAddress { + return EthereumAddress("0x", type: .contractDeployment) } } diff --git a/web3swift/Transaction/Classes/EthereumTransaction.swift b/web3swift/Transaction/Classes/EthereumTransaction.swift index c40ccfd..fc47a43 100644 --- a/web3swift/Transaction/Classes/EthereumTransaction.swift +++ b/web3swift/Transaction/Classes/EthereumTransaction.swift @@ -185,8 +185,15 @@ public struct EthereumTransaction: CustomStringConvertible { if (!from.isValid) { return nil } + var toString: String? = nil + switch self.to.type { + case .normal: + toString = self.to.address.lowercased() + case .contractDeployment: + break + } var params = TransactionParameters(from: from.address.lowercased(), - to: self.to.address.lowercased()) + to: toString) let gasEncoding = self.gasLimit.abiEncode(bits: 256) params.gas = gasEncoding?.toHexString().addHexPrefix().stripLeadingZeroes() let gasPriceEncoding = self.gasPrice.abiEncode(bits: 256) @@ -210,7 +217,12 @@ public struct EthereumTransaction: CustomStringConvertible { static func fromJSON(_ json: [String: Any]) -> EthereumTransaction? { guard let options = Web3Options.fromJSON(json) else {return nil} guard let toString = json["to"] as? String else {return nil} - let to = EthereumAddress(toString) + var to: EthereumAddress + if toString == "0x" || toString == "0x0" { + to = EthereumAddress.contractDeploymentAddress() + } else { + to = EthereumAddress(toString) + } if (!to.isValid) { return nil } @@ -244,19 +256,61 @@ public struct EthereumTransaction: CustomStringConvertible { if (transaction.inferedChainID != nil && transaction.v >= BigUInt(37)) { transaction.chainID = inferedChainID } - let hash = json["hash"] as? String - if hash != nil { - let calculatedHash = transaction.hash - let receivedHash = Data.fromHex(hash!) - if (receivedHash != calculatedHash) { - print("hash mismatch") - print(String(describing: transaction)) - print(json) +// let hash = json["hash"] as? String +// if hash != nil { +// let calculatedHash = transaction.hash +// let receivedHash = Data.fromHex(hash!) +// if (receivedHash != calculatedHash) { +// print("hash mismatch, dat") +// print(String(describing: transaction)) +// print(json) +// return nil +// } +// } + return transaction + } + + static func fromRaw(_ raw: Data) -> EthereumTransaction? { + guard let totalItem = RLP.decode(raw) else {return nil} + guard let rlpItem = totalItem[0] else {return nil} + switch rlpItem.count { + case 9?: + guard let nonceData = rlpItem[0]!.data else {return nil} + let nonce = BigUInt(nonceData) + guard let gasPriceData = rlpItem[1]!.data else {return nil} + let gasPrice = BigUInt(gasPriceData) + guard let gasLimitData = rlpItem[2]!.data else {return nil} + let gasLimit = BigUInt(gasLimitData) + var to:EthereumAddress + switch rlpItem[3]!.content { + case .noItem: + to = EthereumAddress.contractDeploymentAddress() + case .data(let addressData): + if addressData.count == 0 { + to = EthereumAddress.contractDeploymentAddress() + } else if addressData.count == 20 { + to = EthereumAddress(addressData) + } else { + return nil + } + case .list(_, _): return nil } -// assert(receivedHash==calculatedHash) + guard let valueData = rlpItem[4]!.data else {return nil} + let value = BigUInt(valueData) + guard let transactionData = rlpItem[5]!.data else {return nil} + guard let vData = rlpItem[6]!.data else {return nil} + let v = BigUInt(vData) + guard let rData = rlpItem[7]!.data else {return nil} + let r = BigUInt(rData) + guard let sData = rlpItem[8]!.data else {return nil} + let s = BigUInt(sData) + return EthereumTransaction.init(nonce: nonce, gasPrice: gasPrice, gasLimit: gasLimit, to: to, value: value, data: transactionData, v: v, r: r, s: s) + case 6?: + return nil + default: + return nil } - return transaction } static func createRequest(method: JSONRPCmethod, transaction: EthereumTransaction, onBlock: String? = nil, options: Web3Options?) -> JSONRPCrequest? { diff --git a/web3swift/Web3/Classes/Web3+JSONRPC.swift b/web3swift/Web3/Classes/Web3+JSONRPC.swift index 58f5094..a365a5d 100644 --- a/web3swift/Web3/Classes/Web3+JSONRPC.swift +++ b/web3swift/Web3/Classes/Web3+JSONRPC.swift @@ -79,10 +79,10 @@ public struct TransactionParameters: Codable { public var from: String public var gas: String? public var gasPrice: String? - public var to: String + public var to: String? public var value: String? = "0x0" - public init(from _from:String, to _to:String) { + public init(from _from:String, to _to:String?) { from = _from to = _to } diff --git a/web3swift/Web3/Classes/Web3+Protocols.swift b/web3swift/Web3/Classes/Web3+Protocols.swift index 6593603..9c9632f 100644 --- a/web3swift/Web3/Classes/Web3+Protocols.swift +++ b/web3swift/Web3/Classes/Web3+Protocols.swift @@ -14,7 +14,7 @@ public protocol Web3Provider { func send(request: JSONRPCrequest) -> [String:Any]? func send(requests: [JSONRPCrequest]) -> [[String: Any]?]? func sendWithRawResult(request: JSONRPCrequest) -> Data? - var network: Networks? {get} + var network: Networks? {get set} var attachedKeystoreManager: KeystoreManager? {get set} var url: URL {get} } diff --git a/web3swift/Web3/Classes/Web3+Structures.swift b/web3swift/Web3/Classes/Web3+Structures.swift index b73af9d..7f67d27 100644 --- a/web3swift/Web3/Classes/Web3+Structures.swift +++ b/web3swift/Web3/Classes/Web3+Structures.swift @@ -65,7 +65,7 @@ public struct TransactionReceipt { guard let cgu = json["cumulativeGasUsed"] as? String else {return nil} guard let gu = json["gasUsed"] as? String else {return nil} guard let ls = json["logs"] as? Array<[String:AnyObject]> else {return nil} - guard let lbl = json["logsBloom"] as? String else {return nil} + let lbl = json["logsBloom"] as? String let st = json["status"] as? String guard let bnUnwrapped = BigUInt(bn.stripHexPrefix(), radix: 16) else {return nil} @@ -92,9 +92,11 @@ public struct TransactionReceipt { } else { status = TXStatus.failed } - let logsData = Data.fromHex(lbl) - if logsData != nil && logsData!.count > 0 { - logsBloom = EthereumBloomFilter(logsData!) + if lbl != nil { + let logsData = Data.fromHex(lbl!) + if logsData != nil && logsData!.count > 0 { + logsBloom = EthereumBloomFilter(logsData!) + } } } } diff --git a/web3swift/Web3/Classes/Web3+Utils.swift b/web3swift/Web3/Classes/Web3+Utils.swift index 3e04e19..825be3a 100644 --- a/web3swift/Web3/Classes/Web3+Utils.swift +++ b/web3swift/Web3/Classes/Web3+Utils.swift @@ -18,6 +18,15 @@ extension Web3 { } extension Web3.Utils { + public static func calcualteContractAddress(from: EthereumAddress, nonce: BigUInt) -> EthereumAddress? { + guard let normalizedAddress = from.addressData.setLengthLeft(32) else {return nil} + guard let data = RLP.encode([normalizedAddress, nonce] as [Any]) else {return nil} + guard let contractAddressData = Web3.Utils.sha3(data)?[12..<32] else {return nil} + let contractAddress = EthereumAddress(Data(contractAddressData)) + guard contractAddress.isValid else {return nil} + return contractAddress + } + public enum Units { case eth case wei diff --git a/web3swiftTests/web3swiftTests.swift b/web3swiftTests/web3swiftTests.swift index 44bb049..8bdc2f2 100644 --- a/web3swiftTests/web3swiftTests.swift +++ b/web3swiftTests/web3swiftTests.swift @@ -300,6 +300,24 @@ class web3swiftTests: XCTestCase { XCTAssert(encoded == expected, "Failed to RLP encode list of short strings") } + func testRLPdecodeListOfShortStrings() { + let testInput = ["cat","dog"] + var expected = Data() + expected.append(Data([UInt8(0xc8)])) + expected.append(Data([UInt8(0x83)])) + expected.append("cat".data(using: .ascii)!) + expected.append(Data([UInt8(0x83)])) + expected.append("dog".data(using: .ascii)!) + var result = RLP.decode(expected)! + XCTAssert(result.isList, "Failed to RLP decode list of short strings") // we got something non-empty + XCTAssert(result.count == 1, "Failed to RLP decode list of short strings") // we got something non-empty + result = result[0]! + XCTAssert(result.isList, "Failed to RLP decode list of short strings") // we got something non-empty + XCTAssert(result.count == 2, "Failed to RLP decode list of short strings") // we got something non-empty + XCTAssert(result[0]!.data == testInput[0].data(using: .ascii), "Failed to RLP decode list of short strings") + XCTAssert(result[1]!.data == testInput[1].data(using: .ascii), "Failed to RLP decode list of short strings") + } + func testRLPencodeLongString() { let testInput = "Lorem ipsum dolor sit amet, consectetur adipisicing elit" let encoded = RLP.encode(testInput) @@ -310,6 +328,17 @@ class web3swiftTests: XCTestCase { XCTAssert(encoded == expected, "Failed to RLP encode long string") } + func testRLPdecodeLongString() { + let testInput = "Lorem ipsum dolor sit amet, consectetur adipisicing elit" + var expected = Data() + expected.append(Data([UInt8(0xb8)])) + expected.append(Data([UInt8(0x38)])) + expected.append(testInput.data(using: .ascii)!) + let result = RLP.decode(expected)! + XCTAssert(result.count == 1, "Failed to RLP decode long string") + XCTAssert(result[0]!.data == testInput.data(using: .ascii), "Failed to RLP decode long string") + } + func testRLPencodeEmptyString() { let testInput = "" let encoded = RLP.encode(testInput) @@ -318,6 +347,15 @@ class web3swiftTests: XCTestCase { XCTAssert(encoded == expected, "Failed to RLP encode empty string") } + func testRLPdecodeEmptyString() { + let testInput = "" + var expected = Data() + expected.append(Data([UInt8(0x80)])) + let result = RLP.decode(expected)! + XCTAssert(result.count == 1, "Failed to RLP decode empty string") + XCTAssert(result[0]!.data == testInput.data(using: .ascii), "Failed to RLP decode empty string") + } + func testRLPencodeEmptyArray() { let testInput = [Data]() let encoded = RLP.encode(testInput) @@ -326,6 +364,16 @@ class web3swiftTests: XCTestCase { XCTAssert(encoded == expected, "Failed to RLP encode empty array") } + func testRLPdecodeEmptyArray() { +// let testInput = [Data]() + var expected = Data() + expected.append(Data([UInt8(0xc0)])) + var result = RLP.decode(expected)! + XCTAssert(result.count == 1, "Failed to RLP decode empty array") + result = result[0]! + guard case .noItem = result.content else {return XCTFail()} + } + func testRLPencodeShortInt() { let testInput = 15 let encoded = RLP.encode(testInput) @@ -333,6 +381,15 @@ class web3swiftTests: XCTestCase { XCTAssert(encoded == expected, "Failed to RLP encode short int") } + func testRLPdecodeShortInt() { + let testInput = 15 + let expected = Data([UInt8(0x0f)]) + let result = RLP.decode(expected)! + + XCTAssert(result.count == 1, "Failed to RLP decode short int") + XCTAssert(BigUInt(result[0]!.data!) == testInput, "Failed to RLP decode short int") + } + func testRLPencodeLargeInt() { let testInput = 1024 let encoded = RLP.encode(testInput) @@ -343,6 +400,24 @@ class web3swiftTests: XCTestCase { XCTAssert(encoded == expected, "Failed to RLP encode large int") } + func testRLPdecodeLargeInt() { + let testInput = 1024 + var expected = Data() + expected.append(Data([UInt8(0x82)])) + expected.append(Data([UInt8(0x04)])) + expected.append(Data([UInt8(0x00)])) + let result = RLP.decode(expected)! + + XCTAssert(result.count == 1, "Failed to RLP decode large int") + XCTAssert(BigUInt(result[0]!.data!) == testInput, "Failed to RLP decode large int") + } + + func testRLPdecodeTransaction() { + let input = Data.fromHex("0xf90890558504e3b292008309153a8080b9083d6060604052336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550341561004f57600080fd5b60405160208061081d83398101604052808051906020019091905050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141515156100a757600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610725806100f86000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680638da5cb5b14610067578063b2b2c008146100bc578063d59ba0df146101eb578063d8ffdcc414610247575b600080fd5b341561007257600080fd5b61007a61029c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100c757600080fd5b61019460048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506102c1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101d75780820151818401526020810190506101bc565b505050509050019250505060405180910390f35b34156101f657600080fd5b61022d600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080351515906020019091905050610601565b604051808215151515815260200191505060405180910390f35b341561025257600080fd5b61025a6106bf565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6102c96106e5565b6102d16106e5565b6000806000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561032e57600080fd5b8651885114151561033e57600080fd5b875160405180591061034d5750595b9080825280602002602001820160405250935060009250600091505b87518210156105f357600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd87848151811015156103be57fe5b906020019060200201518a858151811015156103d657fe5b906020019060200201518a868151811015156103ee57fe5b906020019060200201516000604051602001526040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b15156104b857600080fd5b6102c65a03f115156104c957600080fd5b50505060405180519050905080156105e65787828151811015156104e957fe5b90602001906020020151848481518110151561050157fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508280600101935050868281518110151561055357fe5b90602001906020020151888381518110151561056b57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff16878481518110151561059957fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff167f334b3b1d4ad406523ee8e24beb689f5adbe99883a662c37d43275de52389da1460405160405180910390a45b8180600101925050610369565b839450505050509392505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561065e57600080fd5b81600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001905092915050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6020604051908101604052806000815250905600a165627a7a723058200618093d895b780d4616f24638637da0e0f9767e6d3675a9525fee1d6ed7f431002900000000000000000000000045245bc59219eeaaf6cd3f382e078a461ff9de7b25a0d1efc3c97d1aa9053aa0f59bf148d73f59764343bf3cae576c8769a14866948da0613d0265634fddd436397bc858e2672653833b57a05cfc8b93c14a6c05166e4a")! + let transaction = EthereumTransaction.fromRaw(input) + print(transaction) + } + func testChecksumAddress() { let input = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359" let output = EthereumAddress.toChecksumAddress(input); @@ -401,17 +476,47 @@ class web3swiftTests: XCTestCase { func testEthSendExampleWithRemoteSigning() { let web3 = Web3.new(URL.init(string: "http://127.0.0.1:8545")!)! + guard case .success(let allAddresses) = web3.eth.getAccounts() else {return XCTFail()} let sendToAddress = EthereumAddress("0x6394b37Cf80A7358b38068f0CA4760ad49983a1B") let contract = web3.contract(Web3.Utils.coldWalletABI, at: sendToAddress, abiVersion: 2) var options = Web3Options.defaultOptions() options.value = Web3.Utils.parseToBigUInt("1.0", units: .eth) - options.from = EthereumAddress("0x804962017f0da9aa3970dc2adbA52A4c22614edB") + options.from = allAddresses[0] let intermediate = contract?.method("fallback", options: options) guard let result = intermediate?.send(password: "") else {return XCTFail()} switch result { - case .success(_): + case .success(let res): + print(res) return case .failure(let error): + print(error) + return XCTFail() + } + } + + func testDeployWithRemoteSigning() { + let web3 = Web3.new(URL.init(string: "http://127.0.0.1:8545")!)! + guard case .success(let allAddresses) = web3.eth.getAccounts() else {return XCTFail()} + 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\"}]" + guard let bytecode = Data.fromHex("6060604052341561000f57600080fd5b6103358061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a16e94bf14610051578063a46b5b6b146100df575b600080fd5b341561005c57600080fd5b61006461013c565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a4578082015181840152602081019050610089565b50505050905090810190601f1680156100d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100ea57600080fd5b61013a600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061020d565b005b610144610250565b6000808073ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102035780601f106101d857610100808354040283529160200191610203565b820191906000526020600020905b8154815290600101906020018083116101e657829003601f168201915b5050505050905090565b806000808073ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001908051906020019061024c929190610264565b5050565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102a557805160ff19168380011785556102d3565b828001600101855582156102d3579182015b828111156102d25782518255916020019190600101906102b7565b5b5090506102e091906102e4565b5090565b61030691905b808211156103025760008160009055506001016102ea565b5090565b905600a165627a7a7230582017359d063cd7fdf56f19ca186a54863ce855c8f070acece905d8538fbbc4d1bf0029") else {return XCTFail()} + let contract = web3.contract(abiString, at: nil, abiVersion: 2) + var options = Web3Options.defaultOptions() + options.from = allAddresses[0] + options.gasLimit = BigUInt(3000000) + let intermediate = contract?.deploy(bytecode: bytecode, options: options) + guard let result = intermediate?.send(password: "") else {return XCTFail()} + switch result { + case .success(let res): + let txHash = res["txhash"]! + print("Transaction with hash " + txHash) + Thread.sleep(forTimeInterval: 1.0) + let receipt = web3.eth.getTransactionReceipt(txHash) + print(receipt) + let details = web3.eth.getTransactionDetails(txHash) + print(details) + return + case .failure(let error): + print(error) return XCTFail() } } @@ -665,7 +770,7 @@ class web3swiftTests: XCTestCase { func testTransactionReceipt() { let web3 = Web3.InfuraMainnetWeb3() - let result = web3.eth.getTransactionReceipt("0x127519412cefd773b952a5413a4467e9119654f59a34eca309c187bd9f3a195a") + let result = web3.eth.getTransactionReceipt("0x83b2433606779fd756417a863f26707cf6d7b2b55f5d744a39ecddb8ca01056e") switch result { case .failure(let error): print(error) @@ -1868,6 +1973,7 @@ class web3swiftTests: XCTestCase { let web3 = Web3.InfuraMainnetWeb3() let userAddress = EthereumAddress("0x6394b37Cf80A7358b38068f0CA4760ad49983a1B") var expected = tokensJSON.count + print(String(expected) + " tokens to update") let semaphore = DispatchSemaphore(value: 0) for token in tokensJSON { let tokenSymbol = token["symbol"] as! String @@ -1897,6 +2003,7 @@ class web3swiftTests: XCTestCase { } OperationQueue.current?.underlyingQueue?.async { expected = expected - 1 + print(String(expected) + " tokens left to update") if expected == 0 { semaphore.signal() }