diff --git a/Podfile b/Podfile index 2491d9c5..b1d79041 100644 --- a/Podfile +++ b/Podfile @@ -1,7 +1,7 @@ def import_pods - pod 'Alamofire', '~> 4.5' + pod 'Alamofire', '~> 4.7' pod 'Alamofire-Synchronous', '~> 4.0' - pod 'BigInt', '~> 3.0.1' + pod 'BigInt', '~> 3.0.2' pod 'CryptoSwift' pod 'Result', '~> 3.0.0' pod 'libsodium' diff --git a/Podfile.lock b/Podfile.lock index ce9fc431..eeeefb5e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,10 +1,10 @@ PODS: - - Alamofire (4.7.0) + - Alamofire (4.7.1) - Alamofire-Synchronous (4.0.0): - Alamofire (~> 4.0) - BigInt (3.0.1): - SipHash (~> 1.2) - - CryptoSwift (0.8.3) + - CryptoSwift (0.9.0) - libsodium (1.0.12) - Result (3.0.0) - secp256k1_ios (0.1.2) @@ -29,10 +29,10 @@ CHECKOUT OPTIONS: :git: https://github.com/shamatar/secp256k1_ios.git SPEC CHECKSUMS: - Alamofire: 907e0a98eb68cdb7f9d1f541a563d6ac5dc77b25 + Alamofire: 68d7d521118d49c615a8d2214d87cdf525599d30 Alamofire-Synchronous: eedf1e6e961c3795a63c74990b3f7d9fbfac7e50 BigInt: 8e8a52161c745cd3ab78e3dc346a9fbee51e6cf6 - CryptoSwift: 033efc3523865d19cc6ab6612fa17a62613b5dfa + CryptoSwift: bca8c5b653dcc2d9734409242a070ff53bafac86 libsodium: 9a8faa5ef2fa0d2d57bd7f7d79bf8fb7c1a9f0ea Result: 1b3e431f37cbcd3ad89c6aa9ab0ae55515fae3b6 secp256k1_ios: 168c6c49ed9771db51875f5255c8f9847e4cf554 diff --git a/web3swift.podspec b/web3swift.podspec index adbd6174..9cd3707c 100644 --- a/web3swift.podspec +++ b/web3swift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "web3swift" -s.version = "0.3.6" +s.version = "0.4.0" s.summary = "Web3 implementation in vanilla Swift for iOS ans macOS" s.description = <<-DESC @@ -14,9 +14,7 @@ s.source = { :git => 'https://github.com/bankex/web3swift.git', :tag = s.social_media_url = 'https://twitter.com/shamatar' s.pod_target_xcconfig = { - 'SWIFT_VERSION' => '4.0', - 'ARCHS' => '${ARCHS_STANDARD_64_BIT}', - 'VALID_ARCHS' => '${ARCHS_STANDARD_64_BIT}' + 'SWIFT_VERSION' => '4.0' } s.module_name = 'web3swift' @@ -26,11 +24,11 @@ s.source_files = "web3swift/**/*.{h,swift}", s.public_header_files = "web3swift/**/*.{h}" -s.dependency 'Alamofire', '~> 4.6' +s.dependency 'Alamofire', '~> 4.7' s.dependency 'Alamofire-Synchronous', '~> 4.0' -s.dependency 'BigInt', '~> 3.0.1' +s.dependency 'BigInt', '~> 3.0.2' s.dependency 'Result', '~> 3.0.0' -s.dependency 'CryptoSwift', '~> 0.8.3' +s.dependency 'CryptoSwift', '~> 0.9.0' s.dependency 'libsodium', '~> 1.0.12' s.dependency 'secp256k1_ios', '~> 0.1.2' diff --git a/web3swift/ABIv2/Classes/ABIv2Decoding.swift b/web3swift/ABIv2/Classes/ABIv2Decoding.swift index 778d7b73..feb3df84 100644 --- a/web3swift/ABIv2/Classes/ABIv2Decoding.swift +++ b/web3swift/ABIv2/Classes/ABIv2Decoding.swift @@ -137,7 +137,7 @@ extension ABIv2Decoder { var eventContent = [String: Any]() eventContent["name"]=event.name let logs = eventLog.topics - var dataForProcessing = eventLog.data + let dataForProcessing = eventLog.data if (logs.count == 1 && event.inputs.count > 0) { return nil } diff --git a/web3swift/ABIv2/Classes/ABIv2Elements.swift b/web3swift/ABIv2/Classes/ABIv2Elements.swift index 72d64bd6..7667c6b5 100644 --- a/web3swift/ABIv2/Classes/ABIv2Elements.swift +++ b/web3swift/ABIv2/Classes/ABIv2Elements.swift @@ -145,19 +145,10 @@ extension ABIv2.Element { } } -extension ABIv2.Element { +extension ABIv2.Element.Event { func decodeReturnedLogs(_ eventLog: EventLog) -> [String:Any]? { - switch self { - case .constructor(_): - return nil - case .event(let event): - guard let eventContent = ABIv2Decoder.decodeLog(event: event, eventLog: eventLog) else {return nil} - return eventContent - case .fallback(_): - return nil - case .function(_): - return nil - } + guard let eventContent = ABIv2Decoder.decodeLog(event: self, eventLog: eventLog) else {return nil} + return eventContent } } diff --git a/web3swift/Contract/Classes/Contract.swift b/web3swift/Contract/Classes/Contract.swift index a25a9a62..0ff008f6 100644 --- a/web3swift/Contract/Classes/Contract.swift +++ b/web3swift/Contract/Classes/Contract.swift @@ -145,4 +145,8 @@ public struct Contract:ContractProtocol { guard case .function(_) = function else {return nil} return function.decodeReturnData(data) } + + public func testBloomForEventPrecence(eventName: String, bloom: EthereumBloomFilter) -> Bool? { + return false + } } diff --git a/web3swift/Contract/Classes/ContractABIv2.swift b/web3swift/Contract/Classes/ContractABIv2.swift index 6ceefe0b..81027fac 100644 --- a/web3swift/Contract/Classes/ContractABIv2.swift +++ b/web3swift/Contract/Classes/ContractABIv2.swift @@ -42,13 +42,13 @@ public struct ContractV2:ContractProtocol { } return toReturn } - public var events: [String: ABIv2.Element] { - var toReturn = [String: ABIv2.Element]() + public var events: [String: ABIv2.Element.Event] { + var toReturn = [String: ABIv2.Element.Event]() for m in self._abi { switch m { case .event(let event): let name = event.name - toReturn[name] = m + toReturn[name] = event default: continue } @@ -133,15 +133,36 @@ public struct ContractV2:ContractProtocol { } public func parseEvent(_ eventLog: EventLog) -> (eventName:String?, eventData:[String:Any]?) { -// for (eName, ev) in self.events { -// let parsed = ev.decodeReturnedLogs(eventLog) -// if parsed != nil { -// return (eName, parsed!) -// } -// } + for (eName, ev) in self.events { + if (!ev.anonymous) { + if eventLog.topics[0] != ev.topic { + continue + } + else { + let parsed = ev.decodeReturnedLogs(eventLog) + if parsed != nil { + return (eName, parsed!) + } + } + } else { + let parsed = ev.decodeReturnedLogs(eventLog) + if parsed != nil { + return (eName, parsed!) + } + } + } return (nil, nil) } + public func testBloomForEventPrecence(eventName: String, bloom: EthereumBloomFilter) -> Bool? { + guard let event = events[eventName] else {return nil} + if event.anonymous { + return true + } + let eventOfSuchTypeIsPresent = bloom.test(topic: event.topic) + return eventOfSuchTypeIsPresent + } + public func decodeReturnData(_ method:String, data: Data) -> [String:Any]? { if method == "fallback" { return [String:Any]() diff --git a/web3swift/Contract/Classes/ContractProtocol.swift b/web3swift/Contract/Classes/ContractProtocol.swift index 80323e97..5b653969 100644 --- a/web3swift/Contract/Classes/ContractProtocol.swift +++ b/web3swift/Contract/Classes/ContractProtocol.swift @@ -17,7 +17,8 @@ public protocol ContractProtocol { init?(_ abiString: String, at: EthereumAddress?) func decodeReturnData(_ method:String, data: Data) -> [String:Any]? func parseEvent(_ eventLog: EventLog) -> (eventName:String?, eventData:[String:Any]?) -// optional func createEventFilter(eventName:String, filter: EventFilter?) + func testBloomForEventPrecence(eventName: String, bloom: EthereumBloomFilter) -> Bool? +// func createEventFilter(eventName:String, filter: EventFilter?) } public struct EventFilter { diff --git a/web3swift/KeystoreManager/Classes/EthereumKeystoreV3.swift b/web3swift/KeystoreManager/Classes/EthereumKeystoreV3.swift index 3ea53b41..98e6f038 100644 --- a/web3swift/KeystoreManager/Classes/EthereumKeystoreV3.swift +++ b/web3swift/KeystoreManager/Classes/EthereumKeystoreV3.swift @@ -49,8 +49,12 @@ public class EthereumKeystoreV3: AbstractKeystore { self.init(jsonData) } - public init?(_ jsonData: Data) { + public convenience init?(_ jsonData: Data) { guard let keystoreParams = try? JSONDecoder().decode(KeystoreParamsV3.self, from: jsonData) else {return nil} + self.init(keystoreParams) + } + + public init?(_ keystoreParams: KeystoreParamsV3) { if (keystoreParams.version != 3) {return nil} if (keystoreParams.crypto.version != nil && keystoreParams.crypto.version != "1") {return nil} self.keystoreParams = keystoreParams diff --git a/web3swift/Web3/Classes/Web3+Contract.swift b/web3swift/Web3/Classes/Web3+Contract.swift index e3e4ad4f..70a46d86 100644 --- a/web3swift/Web3/Classes/Web3+Contract.swift +++ b/web3swift/Web3/Classes/Web3+Contract.swift @@ -12,7 +12,7 @@ import BigInt extension web3 { - public func contract(_ abiString: String, at: EthereumAddress? = nil, abiVersion: Int = 1) -> web3contract? { + public func contract(_ abiString: String, at: EthereumAddress? = nil, abiVersion: Int = 2) -> web3contract? { return web3contract(web3: self, abiString: abiString, at: at, options: self.options, abiVersion: abiVersion) } @@ -21,7 +21,7 @@ extension web3 { var web3 : web3 public var options: Web3Options? = nil - public init?(web3 web3Instance:web3, abiString: String, at: EthereumAddress? = nil, options: Web3Options? = nil, abiVersion: Int = 1) { + public init?(web3 web3Instance:web3, abiString: String, at: EthereumAddress? = nil, options: Web3Options? = nil, abiVersion: Int = 2) { self.web3 = web3Instance self.options = web3.options switch abiVersion { @@ -42,9 +42,6 @@ extension web3 { contract.address = addr } self.options = mergedOptions - if contract.address == nil { - return nil - } } public func method(_ method:String = "fallback", parameters: [AnyObject] = [AnyObject](), extraData: Data = Data(), options: Web3Options?) -> TransactionIntermediate? { @@ -60,7 +57,10 @@ extension web3 { return self.contract.parseEvent(eventLog) } - + public func createEventParser(_ eventName:String, filter:EventFilter?) -> EventParserProtocol? { + let parser = EventParser(web3: self.web3, eventName: eventName, contract: self.contract, filter: filter) + return parser + } } } diff --git a/web3swift/Web3/Classes/Web3+EventParser.swift b/web3swift/Web3/Classes/Web3+EventParser.swift index 2b540291..73324dd5 100644 --- a/web3swift/Web3/Classes/Web3+EventParser.swift +++ b/web3swift/Web3/Classes/Web3+EventParser.swift @@ -10,14 +10,14 @@ import Foundation import Result import BigInt +@available(*, deprecated) public struct EventParser { public struct EventParsingResult { public var event: ABIElement public var receipt: TransactionReceipt public var decodedResult: [String:Any] } - - + public var contract: ContractProtocol public var contractAddress: EthereumAddress? public var event: ABIElement @@ -142,37 +142,51 @@ public struct EventParser { } } -public struct EventParserV2 { - public struct EventParsingResult { - public var event: ABIv2.Element - public var receipt: TransactionReceipt - public var decodedResult: [String:Any] - } - - - public var contract: ContractProtocol - public var contractAddress: EthereumAddress? - public var event: ABIv2.Element - public var filter: EventFilter? - var web3: web3 - public init? (web3 web3Instance: web3, event: ABIv2.Element, contract: ContractProtocol, filter: EventFilter? = nil) { - guard case .event(_) = event else {return nil} - self.event = event - self.web3 = web3Instance - self.contract = contract - self.filter = filter - self.contractAddress = contract.address - } - - - public func parseBlock(_ block: Block) -> Result<[EventParsingResult], Web3Error> { - guard case .event(let ev) = event else {return Result.failure(Web3Error.dataError)} - guard let eventOfSuchTypeIsPresent = block.logsBloom?.test(topic: ev.topic) else {return Result.failure(Web3Error.dataError)} - if (!eventOfSuchTypeIsPresent) { - return Result([]) + +extension web3.web3contract { + public struct EventParser: EventParserProtocol { + public struct EventParserResult:EventParserResultProtocol { + public var eventName: String + public var transactionReceipt: TransactionReceipt + public var contractAddress: EthereumAddress + public var decodedResult: [String:Any] } - var allResults = [EventParsingResult]() - if (self.contractAddress == nil) { + + public var contract: ContractProtocol + public var eventName: String + public var filter: EventFilter? + var web3: web3 + public init? (web3 web3Instance: web3, eventName: String, contract: ContractProtocol, filter: EventFilter? = nil) { + guard let _ = contract.allEvents.index(of: eventName) else {return nil} + self.eventName = eventName + self.web3 = web3Instance + self.contract = contract + self.filter = filter + } + + public func parseBlockByNumber(_ blockNumber: UInt64) -> Result<[EventParserResultProtocol], Web3Error> { + let response = web3.eth.getBlockByNumber(blockNumber) + switch response { + case .success(let block): + return parseBlock(block) + case .failure(let error): + return Result.failure(error) + } + } + + public func parseBlock(_ block: Block) -> Result<[EventParserResultProtocol], Web3Error> { + guard let bloom = block.logsBloom else {return Result.failure(Web3Error.dataError)} + if self.contract.address != nil { + let addressPresent = block.logsBloom?.test(topic: self.contract.address!.addressData) + if (addressPresent != true) { + return Result([EventParserResultProtocol]()) + } + } + guard let eventOfSuchTypeIsPresent = self.contract.testBloomForEventPrecence(eventName: self.eventName, bloom: bloom) else {return Result.failure(Web3Error.dataError)} + if (!eventOfSuchTypeIsPresent) { + return Result([EventParserResultProtocol]()) + } + var allResults = [EventParserResultProtocol]() for transaction in block.transactions { switch transaction { case .null: @@ -196,81 +210,58 @@ public struct EventParserV2 { } } } - } else { - for transaction in block.transactions { - switch transaction { - case .null: - return Result.failure(Web3Error.dataError) - case .transaction(let tx): - guard let hash = tx.hash else {return Result.failure(Web3Error.dataError)} - if (tx.to != self.contractAddress) { - continue - } - let subresult = parseTransactionByHash(hash) - switch subresult { - case .failure(let error): - return Result.failure(error) - case .success(let subsetOfEvents): - allResults += subsetOfEvents - } - case .hash(let hash): - let response = self.web3.eth.getTransactionDetails(hash) - switch response { - case .failure(let error): - return Result.failure(error) - case .success(let details): - guard let hash = details.transaction.hash else {return Result.failure(Web3Error.dataError)} - let to = details.transaction.to - if (to != self.contractAddress) { - continue - } - let subresult = parseTransactionByHash(hash) - switch subresult { - case .failure(let error): - return Result.failure(error) - case .success(let subsetOfEvents): - allResults += subsetOfEvents - } + return Result(allResults) + } + + public func parseTransactionByHash(_ hash: Data) -> Result<[EventParserResultProtocol], Web3Error> { + let response = web3.eth.getTransactionReceipt(hash) + switch response { + case .failure(let error): + return Result.failure(error) + case .success(let receipt): + guard let bloom = receipt.logsBloom else {return Result.failure(Web3Error.dataError)} + if self.contract.address != nil { + let addressPresent = bloom.test(topic: self.contract.address!.addressData) + if (addressPresent != true) { + return Result([EventParserResultProtocol]()) } } - } - } - return Result(allResults) - } - - public func parseTransactionByHash(_ hash: Data) -> Result<[EventParsingResult], Web3Error> { - let response = web3.eth.getTransactionReceipt(hash) - switch response { - case .failure(let error): - return Result.failure(error) - case .success(let receipt): - guard case .event(let ev) = event else {return Result.failure(Web3Error.dataError)} - guard let eventOfSuchTypeIsPresent = receipt.logsBloom?.test(topic: ev.topic) else {return Result.failure(Web3Error.dataError)} - if (!eventOfSuchTypeIsPresent) { - return Result([]) - } - let decodedLogs = receipt.logs.flatMap({ (log) -> [String:Any]? in - self.event.decodeReturnedLogs(log) - }) - var allResults = [EventParsingResult]() - if (self.filter != nil) { - for log in decodedLogs { - let parsingResult = EventParsingResult(event: self.event, receipt: receipt, decodedResult: log) - allResults.append(parsingResult) + guard let eventOfSuchTypeIsPresent = self.contract.testBloomForEventPrecence(eventName: self.eventName, bloom: bloom) else {return Result.failure(Web3Error.dataError)} + if (!eventOfSuchTypeIsPresent) { + return Result([EventParserResultProtocol]()) } - } else { - for log in decodedLogs { - let parsingResult = EventParsingResult(event: self.event, receipt: receipt, decodedResult: log) - allResults.append(parsingResult) + var allLogs = receipt.logs + if (self.contract.address != nil) { + allLogs = receipt.logs.filter({ (log) -> Bool in + log.address == self.contract.address + }) } + let decodedLogs = allLogs.flatMap({ (log) -> EventParserResultProtocol? in + let (n, d) = contract.parseEvent(log) + guard let evName = n, let evData = d else {return nil} + return EventParserResult(eventName: evName, transactionReceipt: receipt, contractAddress: log.address, decodedResult: evData) + }).filter { (res) -> Bool in + return res != nil && res.eventName == self.eventName + } + var allResults = [EventParserResultProtocol]() + if (self.filter != nil) { + // TODO NYI + allResults = decodedLogs +// for log in decodedLogs { +// let parsingResult = EventParserResult(eventName: self.eventName, transactionReceipt: receipt, decodedResult: log) +// allResults.append(parsingResult) +// } + } else { + allResults = decodedLogs + } + return Result(allResults) } - return Result(allResults) } - } - - public func parseTransaction(_ transaction: EthereumTransaction) -> Result<[EventParsingResult], Web3Error> { - guard let hash = transaction.hash else {return Result.failure(Web3Error.dataError)} - return self.parseTransactionByHash(hash) + + public func parseTransaction(_ transaction: EthereumTransaction) -> Result<[EventParserResultProtocol], Web3Error> { + guard let hash = transaction.hash else {return Result.failure(Web3Error.dataError)} + return self.parseTransactionByHash(hash) + } } } diff --git a/web3swift/Web3/Classes/Web3+Protocols.swift b/web3swift/Web3/Classes/Web3+Protocols.swift index b332a4a2..5cea9676 100644 --- a/web3swift/Web3/Classes/Web3+Protocols.swift +++ b/web3swift/Web3/Classes/Web3+Protocols.swift @@ -8,6 +8,7 @@ import Foundation import BigInt +import Result public protocol Web3Provider { func send(request: JSONRPCrequest) -> [String:Any]? @@ -17,6 +18,20 @@ public protocol Web3Provider { var url: URL {get} } +public protocol EventParserResultProtocol { + var eventName: String {get} + var decodedResult: [String:Any] {get} + var contractAddress: EthereumAddress {get} + var transactionReceipt: TransactionReceipt {get} +} + +public protocol EventParserProtocol { + func parseTransaction(_ transaction: EthereumTransaction) -> Result<[EventParserResultProtocol], Web3Error> + func parseTransactionByHash(_ hash: Data) -> Result<[EventParserResultProtocol], Web3Error> + func parseBlock(_ block: Block) -> Result<[EventParserResultProtocol], Web3Error> + func parseBlockByNumber(_ blockNumber: UInt64) -> Result<[EventParserResultProtocol], Web3Error> +} + public enum Networks { case Rinkeby case Mainnet diff --git a/web3swift/Web3/Classes/Web3+Utils.swift b/web3swift/Web3/Classes/Web3+Utils.swift index 60e06998..21a98f7f 100644 --- a/web3swift/Web3/Classes/Web3+Utils.swift +++ b/web3swift/Web3/Classes/Web3+Utils.swift @@ -109,11 +109,11 @@ extension Web3.Utils { return hash } - public static func parseToBigUInt(_ amount: String, toUnits: Web3.Utils.Units = .eth) -> BigUInt? { + public static func parseToBigUInt(_ amount: String, units: Web3.Utils.Units = .eth) -> BigUInt? { let separators = CharacterSet(charactersIn: ".,") let components = amount.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: separators) guard components.count == 1 || components.count == 2 else {return nil} - let unitDecimals = toUnits.decimals + let unitDecimals = units.decimals guard let beforeDecPoint = BigUInt(components[0], radix: 10) else {return nil} var mainPart = beforeDecPoint*BigUInt(10).power(unitDecimals) if (components.count == 2) { diff --git a/web3swiftTests/web3swiftTests.swift b/web3swiftTests/web3swiftTests.swift index 65acd8da..004f2304 100644 --- a/web3swiftTests/web3swiftTests.swift +++ b/web3swiftTests/web3swiftTests.swift @@ -328,7 +328,7 @@ class web3swiftTests: XCTestCase { XCTAssert(encoded == expected, "Failed to RLP encode large int") } - func testChecksubAddress() { + func testChecksumAddress() { let input = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359" let output = EthereumAddress.toChecksumAddress(input); XCTAssert(output == "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", "Failed to checksum address") @@ -364,6 +364,26 @@ class web3swiftTests: XCTestCase { } } + func testEthSendExample() { + let web3 = Web3.InfuraMainnetWeb3() + let sendToAddress = EthereumAddress("0x6394b37Cf80A7358b38068f0CA4760ad49983a1B") + let tempKeystore = try! EthereumKeystoreV3(password: "") + let keystoreManager = KeystoreManager([tempKeystore!]) + web3.addKeystoreManager(keystoreManager) + 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 = keystoreManager.addresses?.first + let intermediate = contract?.method("fallback", options: options) + guard let result = intermediate?.send(password: "") else {return XCTFail()} + switch result { + case .success(_): + return XCTFail() + case .failure(let error): + guard case .unknownError = error else {return XCTFail()} + } + } + func testERC20Encode() { let jsonString = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"},{\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"approveAndCall\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialAmount\",\"type\":\"uint256\"},{\"name\":\"_tokenName\",\"type\":\"string\"},{\"name\":\"_decimalUnits\",\"type\":\"uint8\"},{\"name\":\"_tokenSymbol\",\"type\":\"string\"}],\"type\":\"constructor\"},{\"payable\":false,\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},]" do { @@ -1144,71 +1164,52 @@ class web3swiftTests: XCTestCase { func testEventParsing1usingABIv2() { let jsonString = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"},{\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"approveAndCall\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialAmount\",\"type\":\"uint256\"},{\"name\":\"_tokenName\",\"type\":\"string\"},{\"name\":\"_decimalUnits\",\"type\":\"uint8\"},{\"name\":\"_tokenSymbol\",\"type\":\"string\"}],\"type\":\"constructor\"},{\"payable\":false,\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},]" + let contractAddress = EthereumAddress("0x45245bc59219eeaaf6cd3f382e078a461ff9de7b") let web3 = Web3.InfuraMainnetWeb3() - let response = web3.eth.getBlockByNumber(UInt64(5200088), fullTransactions: true) - switch response { - case .failure(_): - XCTFail() - case .success(let result): - print(result) - let contractAddress = EthereumAddress("0x45245bc59219eeaaf6cd3f382e078a461ff9de7b") - let contract = ContractV2(jsonString, at: contractAddress) - let event = contract?.events["Transfer"] - let parser = EventParserV2(web3: web3, event: event!, contract: contract!, filter: nil) - let present = parser!.parseBlock(result) - guard case .success(let pres) = present else {return XCTFail()} - print(pres) - XCTAssert(pres.count == 1) - let decoded = pres[0].decodedResult - XCTAssert(decoded["name"] as! String == "Transfer") - XCTAssert(decoded["_to"] as! EthereumAddress == EthereumAddress("0xa5dcf6e0fee38f635c4a8d50d90e24400ed547d2")) - XCTAssert(decoded["_from"] as! EthereumAddress == EthereumAddress("0xdbf493e8d7db835192c02b992bd1ab72e96fd2e3")) - XCTAssert(decoded["_value"] as! BigUInt == BigUInt("3946fe37ffce3a0000", radix: 16)!) - } + let contract = web3.contract(jsonString, at: contractAddress, abiVersion: 2) + guard let eventParser = contract?.createEventParser("Transfer", filter: nil) else {return XCTFail()} + let present = eventParser.parseBlockByNumber(UInt64(5200088)) + guard case .success(let pres) = present else {return XCTFail()} + print(pres) + XCTAssert(pres.count == 1) + let decoded = pres[0].decodedResult + XCTAssert(decoded["name"] as! String == "Transfer") + XCTAssert(decoded["_to"] as! EthereumAddress == EthereumAddress("0xa5dcf6e0fee38f635c4a8d50d90e24400ed547d2")) + XCTAssert(decoded["_from"] as! EthereumAddress == EthereumAddress("0xdbf493e8d7db835192c02b992bd1ab72e96fd2e3")) + XCTAssert(decoded["_value"] as! BigUInt == BigUInt("3946fe37ffce3a0000", radix: 16)!) + XCTAssert(pres[0].contractAddress == EthereumAddress("0x45245bc59219eeaaf6cd3f382e078a461ff9de7b")) + XCTAssert(pres[0].transactionReceipt.transactionHash.toHexString().addHexPrefix() == "0xcb235e8c6ecda032bc82c1084d2159ab82e7e4de35be703da6e80034bc577673") } func testEventParsing2usingABIv2() { let jsonString = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"},{\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"approveAndCall\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialAmount\",\"type\":\"uint256\"},{\"name\":\"_tokenName\",\"type\":\"string\"},{\"name\":\"_decimalUnits\",\"type\":\"uint8\"},{\"name\":\"_tokenSymbol\",\"type\":\"string\"}],\"type\":\"constructor\"},{\"payable\":false,\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},]" let web3 = Web3.InfuraMainnetWeb3() - let response = web3.eth.getBlockByNumber(UInt64(5200120), fullTransactions: false) - switch response { - case .failure(_): - XCTFail() - case .success(let result): - let contractAddress = EthereumAddress("0x45245bc59219eeaaf6cd3f382e078a461ff9de7b") - let contract = ContractV2(jsonString, at: nil) - let event = contract?.events["Transfer"] - let parser = EventParserV2(web3: web3, event: event!, contract: contract!, filter: nil) - let present = parser!.parseBlock(result) - guard case .success(let pres) = present else {return XCTFail()} - print(pres) - XCTAssert(pres.count == 81) - } + let contract = web3.contract(jsonString, at: nil, abiVersion: 2) + guard let eventParser = contract?.createEventParser("Transfer", filter: nil) else {return XCTFail()} + let present = eventParser.parseBlockByNumber(UInt64(5200120)) + guard case .success(let pres) = present else {return XCTFail()} + print(pres) + XCTAssert(pres.count == 81) } - + func testEventParsing3usingABIv2() { let jsonString = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"},{\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"approveAndCall\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialAmount\",\"type\":\"uint256\"},{\"name\":\"_tokenName\",\"type\":\"string\"},{\"name\":\"_decimalUnits\",\"type\":\"uint8\"},{\"name\":\"_tokenSymbol\",\"type\":\"string\"}],\"type\":\"constructor\"},{\"payable\":false,\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},]" let web3 = Web3.InfuraMainnetWeb3() + let contract = web3.contract(jsonString, at: nil, abiVersion: 2) + guard let eventParser = contract?.createEventParser("Transfer", filter: nil) else {return XCTFail()} let blockNumber = web3.eth.getBlockNumber() guard case .success(let currentBlock) = blockNumber else {return XCTFail()} let currentBlockAsInt = UInt64(currentBlock) for i in currentBlockAsInt-3 ... currentBlockAsInt { - let response = web3.eth.getBlockByNumber(i, fullTransactions: false) - switch response { - case .failure(_): - XCTFail() - case .success(let result): - let contract = ContractV2(jsonString, at: nil) - let event = contract?.events["Transfer"] - let parser = EventParserV2(web3: web3, event: event!, contract: contract!, filter: nil) - let present = parser!.parseBlock(result) - guard case .success(let pres) = present else {return XCTFail()} - for p in pres { - print("Block " + String(i) + "\n") - print("From " + (p.decodedResult["_from"] as! EthereumAddress).address + "\n") - print("From " + (p.decodedResult["_to"] as! EthereumAddress).address + "\n") - print("Value " + String(p.decodedResult["_value"] as! BigUInt) + "\n") - } + let present = eventParser.parseBlockByNumber(i) + guard case .success(let pres) = present else {return XCTFail()} + for p in pres { + print("Block " + String(i) + "\n") + print("Emitted by contract " + p.contractAddress.address + "\n") + print("TX hash " + p.transactionReceipt.transactionHash.toHexString().addHexPrefix() + "\n") + print("From " + (p.decodedResult["_from"] as! EthereumAddress).address + "\n") + print("From " + (p.decodedResult["_to"] as! EthereumAddress).address + "\n") + print("Value " + String(p.decodedResult["_value"] as! BigUInt) + "\n") } } }