diff --git a/CHANGELOG.md b/CHANGELOG.md index 782105f30..8724a8628 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Change Log +## [Unreleased](https://github.com/matter-labs/web3swift/tree/HEAD) + +[Full Changelog](https://github.com/matter-labs/web3swift/compare/2.2.0...HEAD) + +**Closed issues:** + +- EIP67Code missing in web3swift 2.1.6 [\#176](https://github.com/matter-labs/web3swift/issues/176) +- ENS initializer is inaccessible due to `internal` protection level [\#171](https://github.com/matter-labs/web3swift/issues/171) + +**Merged pull requests:** + +- Update ETHRegistrarController.swift [\#183](https://github.com/matter-labs/web3swift/pull/183) ([barrasso](https://github.com/barrasso)) +- Fix typo in Usage [\#182](https://github.com/matter-labs/web3swift/pull/182) ([sweepty](https://github.com/sweepty)) +- Expose errorDescription. [\#181](https://github.com/matter-labs/web3swift/pull/181) ([andresousa](https://github.com/andresousa)) +- Update ENS Resolver [\#180](https://github.com/matter-labs/web3swift/pull/180) ([barrasso](https://github.com/barrasso)) + +## [2.2.0](https://github.com/matter-labs/web3swift/tree/2.2.0) (2019-04-30) +[Full Changelog](https://github.com/matter-labs/web3swift/compare/2.1.6...2.2.0) + +**Closed issues:** + +- Failed to fetch gas estimate [\#178](https://github.com/matter-labs/web3swift/issues/178) + +**Merged pull requests:** + +- 2.2.0 [\#179](https://github.com/matter-labs/web3swift/pull/179) ([BaldyAsh](https://github.com/BaldyAsh)) + +## [2.1.6](https://github.com/matter-labs/web3swift/tree/2.1.6) (2019-04-26) +[Full Changelog](https://github.com/matter-labs/web3swift/compare/2.1.5...2.1.6) + +**Merged pull requests:** + +- 2.1.6 [\#175](https://github.com/matter-labs/web3swift/pull/175) ([BaldyAsh](https://github.com/BaldyAsh)) +- Quickfix ens [\#174](https://github.com/matter-labs/web3swift/pull/174) ([BaldyAsh](https://github.com/BaldyAsh)) + ## [2.1.5](https://github.com/matter-labs/web3swift/tree/2.1.5) (2019-04-24) [Full Changelog](https://github.com/matter-labs/web3swift/compare/2.1.4...2.1.5) diff --git a/Cartfile b/Cartfile index d76dd3096..0a1f7ed2b 100755 --- a/Cartfile +++ b/Cartfile @@ -1,4 +1,4 @@ github "mxcl/PromiseKit" ~> 6.8.4 -github "attaswift/BigInt" ~> 3.1 +github "attaswift/BigInt" ~> 4.0 github "daltoniam/Starscream" ~> 3.1.0 github "krzyzanowskim/CryptoSwift" ~> 1.0.0 diff --git a/Documentation/Usage.md b/Documentation/Usage.md index a5310f799..15285fe33 100755 --- a/Documentation/Usage.md +++ b/Documentation/Usage.md @@ -46,10 +46,12 @@ - [Infura Websocket interactions](#infura-websocket-interactions) - [Connect to Infura endpoint](#connect-to-infura-endpoint) - [Connect to custom Infura-like endpoint](#connect-to-custom-infura-like-endpoint) - - [Create a filter in the node to notify when something happened](#create-a-filter-in-the-node-to-notify-when-something-happened) + - [Set a filter in the node to notify when something happened](#set-a-filter-in-the-node-to-notify-when-something-happened) - [Get new pending transactions](#get-new-pending-transactions) - [Create a new subscription over particular events](#create-a-new-subscription-over-particular-events) - [Subscribe on new pending transactions](#subscribe-on-new-pending-transactions) + - [Subscribe on logs](https://github.com/matter-labs/web3swift/blob/master/Documentation/Usage.md#subscribe-on-logs) + - [Subscribe on new heads](https://github.com/matter-labs/web3swift/blob/master/Documentation/Usage.md#subscribe-on-new-heads) - [ENS](#ens) - [Registry](#registry) - [Resolver](#resolver) @@ -181,7 +183,7 @@ if wallet.isHD { ```swift let password = "web3swift" let ethereumAddress = EthereumAddress(wallet.address)! -let pkData = try! keysoreManager.UNSAFE_getPrivateKeyData(password: password, account: ethereumAddress).toHexString() +let pkData = try! keystoreManager.UNSAFE_getPrivateKeyData(password: password, account: ethereumAddress).toHexString() ``` ## Ethereum Endpoints interaction @@ -421,18 +423,28 @@ socketProvider = InfuraWebsocketProvider.connectToInfuraSocket(.Mainnet, delegat socketProvider = InfuraWebsocketProvider.connectToSocket("ws://your.endpoint", delegate: delegate) ``` -#### Create a filter in the node to notify when something happened +#### Set a filter in the node to notify when something happened To study possible filters read [Infura WSS filters documentation](https://infura.io/docs/ethereum/wss/introduction) ```swift -try! socketProvider.filter(method: , params: <[Encodable]?>) +// Getting logs +try! socketProvider.setFilterAndGetLogs(method: , params: <[Encodable]?>) +// Getting changes +try! socketProvider.setFilterAndGetChanges(method: , params: <[Encodable]?>) +``` +Or you can provide parameters in more convenient way: +```swift +// Getting logs +try! socketProvider.setFilterAndGetLogs(method: , address: , fromBlock: , toBlock: , topics: <[String]?>) +// Getting changes +try! socketProvider.setFilterAndGetChanges(method: , address: , fromBlock: , toBlock: , topics: <[String]?>) ``` #### Get new pending transactions ```swift -try! socketProvider.filter(method: .newPendingTransactionFilter) +try! socketProvider.setFilterAndGetLogs(method: .newPendingTransactionFilter) ``` #### Create a new subscription over particular events @@ -449,6 +461,18 @@ try! socketProvider.subscribe(params: <[Encodable]>) try! socketProvider.subscribeOnNewPendingTransactions() ``` +#### Subscribe on logs + +```swift +try! socketProvider.subscribeOnLogs(addresses: <[EthereumAddress]?>, topics: <[String]?>) +``` + +#### Subscribe on new heads + +```swift +try! socketProvider.subscribeOnNewHeads() +``` + ## ENS You need ENS instance for future actions: diff --git a/README.md b/README.md index 54b194362..880c15ba9 100755 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ [![Build Status](https://travis-ci.com/matter-labs/web3swift.svg?branch=develop)](https://travis-ci.com/matter-labs/web3swift) [![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg?style=flat)](https://developer.apple.com/swift/) -[![Platform](https://img.shields.io/cocoapods/p/web3swift.svg?style=flat)](http://cocoapods.org/pods/web3swift) -[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/web3swift.svg?style=flat)](http://cocoapods.org/pods/web3swift) +[![Platform](https://img.shields.io/cocoapods/p/web3swift.svg?style=flat)](http://cocoapods.org/pods/web3.swift.pod) +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/web3.swift.pod.svg?style=flat)](http://cocoapods.org/pods/web3.swift.pod) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -[![License](https://img.shields.io/cocoapods/l/web3swift.svg?style=flat)](http://cocoapods.org/pods/web3swift) +[![License](https://img.shields.io/cocoapods/l/web3swift.svg?style=flat)](http://cocoapods.org/pods/web3.swift.pod) [![support](https://brianmacdonald.github.io/Ethonate/svg/eth-support-blue.svg)](https://brianmacdonald.github.io/Ethonate/address#0xe22b8979739d724343bd002f9f432f5990879901) [![Stackoverflow](https://img.shields.io/badge/stackoverflow-ask-blue.svg)](https://stackoverflow.com/questions/tagged/web3swift) @@ -17,73 +17,24 @@ -- [Documentation](#documentation) -- [What we have already done:](#what-we-have-already-done) - [Design Decisions](#design-decisions) -- [Requirements](#requirements) -- [Migration Guides](#migration-guides) -- [Communication](#communication) +- [Projects that using web3swift](#projects-that-using-web3swift) - [Installation](#installation) - - [CocoaPods](#cocoapods) - - [Carthage](#carthage) -- [Example Project](#example-project) -- [Popular questions](#popular-questions) - - [Is it possible to get Mnemonic Phrase (Seed Phrase) from Private key using web3swift?](#is-it-possible-to-get-mnemonic-phrase-seed-phrase-from-private-key-using-web3swift) - - [How to interact with custom smart-contract with web3swift?](#how-to-interact-with-custom-smart-contract-with-web3swift) - - [How to set test local node?](#how-to-set-test-local-node) -- [What's next](#whats-next) + - [Requirements](#requirements) + - [Migration Guides](#migration-guides) +- [Documentation](#documentation) + - [Example](#example) +- [FAQ](#faq) - [Credits](#credits) - - [Security Disclosure](#security-disclosure) -- [Donations](#donations) + - [What we have already done](#what-we-have-already-done) + - [Future steps](#future-steps) +- [Contribute](#contribute) +- [Security Disclosure](#security-disclosure) - [License](#license) -## What we have already done: - -- [x] Swift implementation of [web3.js](https://github.com/ethereum/web3.js/) functionality :zap: -- [x] Interaction with remote node via JSON RPC :thought_balloon: -- [x] Local keystore management (`geth` compatible) -- [x] Smart-contract ABI parsing :book: -- [x] ABI deconding (V2 is supported with return of structures from public functions. Part of 0.4.22 Solidity compiler) -- [x] Ethereum Name Service (ENS) support - a secure & decentralised way to address resources both on and off the blockchain using simple, human-readable names -- [x] Interactions (read/write to Smart contracts) :arrows_counterclockwise: -- [x] Complete Infura Ethereum API support, patial Websockets API support -- [x] Parsing TxPool content into native values (ethereum addresses and transactions) - easy to get pending transactions -- [x] Event loops functionality -- [x] Supports Web3View functionality - WKWebView with injected "web3" provider -- [x] Possibility to add or remove "middleware" that intercepts, modifies and even cancel transaction workflow on stages "before assembly", "after assembly"and "before submission" -- [x] Literally following the standards: - - [x] [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) HD Wallets: Deterministic Wallet - - [x] [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) (Seed phrases) - - [x] [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) (Key generation prefixes) - - [x] [EIP-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) (A standard interface for tokens - ERC-20) - - [x] [EIP-67](https://github.com/ethereum/EIPs/issues/67) (Standard URI scheme with metadata, value and byte code) - - [x] [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) (Replay attacks protection) *enforced!* - - [x] [EIP-681](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-681.md) (A standard way of representing various transactions, especially payment requests in Ethers and ERC-20 tokens as URLs) - - [x] [EIP-721](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md) (A standard interface for non-fungible tokens, also known as deeds - ERC-721) - - [x] [EIP-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) (Standard Interface Detection, also known as ERC-165) - - [x] [EIP-777](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-777.md) (New Advanced Token Standard, also known as ERC-777) - - [x] [EIP-820](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-820.md) (Pseudo-introspection Registry Contract, also known as ERC-820) - - [x] [EIP-888](https://github.com/ethereum/EIPs/issues/888) (MultiDimensional Token Standard, also known as ERC-888) - - [x] [EIP-1400](https://github.com/ethereum/EIPs/issues/1411) (Security Token Standard, also known as ERC-1400) - - [x] [EIP-1410](https://github.com/ethereum/EIPs/issues/1410) (Partially Fungible Token Standard, also known as ERC-1410) - - [x] [EIP-1594](https://github.com/ethereum/EIPs/issues/1594) (Core Security Token Standard, also known as ERC-1594) - - [x] [EIP-1643](https://github.com/ethereum/EIPs/issues/1643) (Document Management Standard, also known as ERC-1643) - - [x] [EIP-1644](https://github.com/ethereum/EIPs/issues/1644) (Controller Token Operation Standard, also known as ERC-1644) - - [x] [EIP-1633](https://github.com/ethereum/EIPs/issues/1634) (Re-Fungible Token, also known as ERC-1633) - - [x] [EIP-721x](https://github.com/loomnetwork/erc721x) (An extension of ERC721 that adds support for multi-fungible tokens and batch transfers, while being fully backward-compatible, also known as ERC-721x) - - [x] [EIP-1155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md) (Multi Token Standard, also known as ERC-1155) - - [x] [EIP-1376](https://github.com/ethereum/EIPs/issues/1376) (Service-Friendly Token, also known as ERC-1376) - - [x] [ST-20](https://github.com/PolymathNetwork/polymath-core) - ST-20 token is an Ethereum-based token implemented on top of the ERC-20 protocol that adds the ability for tokens to control transfers based on specific rules - -- [x] RLP encoding -- [x] Batched requests in concurrent mode -- [x] Base58 encoding scheme -- [x] Formatting to and from Ethereum Units -- [x] Comprehensive Unit and Integration Test Coverage - ## Design Decisions - Not every JSON RPC function is exposed yet, and priority is given to the ones required for mobile devices @@ -91,16 +42,6 @@ - Requirements for password input on every transaction are indeed a design decision. Interface designers can save user passwords with the user's consent - Public function for private key export is exposed for user convenience but marked as UNSAFE_ :) Normal workflow takes care of EIP155 compatibility and proper clearing of private key data from memory -## Requirements - -- iOS 9.0+ / macOS 10.11+ -- Xcode 10.2+ -- Swift 5.0+ - -## Migration Guides - -- [web3swift 2.0 Migration Guide](https://github.com/matterinc/web3swift/blob/master/Documentation/web3swift%202.0%20Migration%20Guide.md) - ## Projects that using web3swift If you are using this library in your project, please [add a link](https://github.com/matter-labs/web3swift/edit/develop/README.md) to this repo. @@ -125,7 +66,7 @@ If you are using this library in your project, please [add a link](https://githu ## Installation -### CocoaPods +- CocoaPods [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: @@ -141,7 +82,7 @@ platform :ios, '9.0' target '' do use_frameworks! - pod 'web3swift', '~> 2.1.3' + pod 'web3.swift.pod', '~> 2.2.0' end ``` @@ -151,7 +92,7 @@ Then, run the following command: $ pod install ``` -### Carthage +- Carthage [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. @@ -179,6 +120,17 @@ github "matter-labs/web3swift" "master" Run `carthage update` to build the framework. By default, Carthage performs checkouts and builds in a new directory 'Carthage' in the same location as your Cartfile. Open this directory, go to 'Build' directory, choose iOS or macOS directory and use the framework from the chosen directory in your Xcode project. +### Requirements + +- iOS 9.0+ / macOS 10.11+ +- Xcode 10.2+ +- Swift 5.0+ + +### Migration Guides + +- [web3swift 2.0 Migration Guide](https://github.com/matterinc/web3swift/blob/master/Documentation/web3swift%202.0%20Migration%20Guide.md) + + --- ## Documentation @@ -236,7 +188,7 @@ Here are quick references for basic features: - [ReverseRegistrar](https://github.com/matter-labs/web3swift/blob/master/Documentation/Usage.md#reverseregistrar) -## Example Project +### Example You can try lib by running the example project: @@ -245,9 +197,9 @@ You can try lib by running the example project: - Install Dependencies: `pod install` - Open: `open ./web3swiftExample.xcworkspace` -## Popular questions +## FAQ -#### Is it possible to get a Mnemonic Phrase (Seed Phrase) from Private key using web3swift? +> Is it possible to get a Mnemonic Phrase (Seed Phrase) from Private key using web3swift? In web3swift there is no backward conversion from Private key to Mnemonic Phrase. Also it is theoretically impossible to recover a phrase from a Private key. After Seed Phrase is converted to some initial entropy the “master key is derived” and the initial entropy is discarded. @@ -255,7 +207,7 @@ The simplest solution is to encrypt the phrase using users pincode and save it i Mnemonic Phrase is very sensitive data and you must be very careful to let the user get it. Our advise if you want to show it to a user - ask to save a Passphrase when creating BIP32Keystore. -#### How to interact with custom smart-contract with web3swift? +> How to interact with custom smart-contract with web3swift? For example: you want to interact with smart-contract and all you know is - its address (address example: 0xfa28eC7198028438514b49a3CF353BcA5541ce1d). @@ -289,7 +241,7 @@ let transactionOptions: TransactionOptions = let transaction = contract.write(method, parameters: parameters, extraData: extraData, transactionOptions: transactionOptions) ``` -#### How to set test local node? +> How to set test local node? You can write something like that: ```swift func setLocalNode(port: Int = 8545) -> Web3? { @@ -298,7 +250,61 @@ func setLocalNode(port: Int = 8545) -> Web3? { } ``` -## What's next +## Credits + +Alex Vlasov, [@shamatar](https://github.com/shamatar) + +Petr Korolev, [@skywinder](https://github.com/skywinder) + +Anton Grigorev, [@baldyash](https://github.com/BaldyAsh) + + +### What we have already done + +- [x] Swift implementation of [web3.js](https://github.com/ethereum/web3.js/) functionality :zap: +- [x] Interaction with remote node via JSON RPC :thought_balloon: +- [x] Local keystore management (`geth` compatible) +- [x] Smart-contract ABI parsing :book: +- [x] ABI deconding (V2 is supported with return of structures from public functions. Part of 0.4.22 Solidity compiler) +- [x] Ethereum Name Service (ENS) support - a secure & decentralised way to address resources both on and off the blockchain using simple, human-readable names +- [x] Interactions (read/write to Smart contracts) :arrows_counterclockwise: +- [x] Complete Infura Ethereum API support, patial Websockets API support +- [x] Parsing TxPool content into native values (ethereum addresses and transactions) - easy to get pending transactions +- [x] Event loops functionality +- [x] Supports Web3View functionality - WKWebView with injected "web3" provider +- [x] Possibility to add or remove "middleware" that intercepts, modifies and even cancel transaction workflow on stages "before assembly", "after assembly"and "before submission" +- [x] Literally following the standards: + - [x] [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) HD Wallets: Deterministic Wallet + - [x] [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) (Seed phrases) + - [x] [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) (Key generation prefixes) + - [x] [EIP-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) (A standard interface for tokens - ERC-20) + - [x] [EIP-67](https://github.com/ethereum/EIPs/issues/67) (Standard URI scheme with metadata, value and byte code) + - [x] [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) (Replay attacks protection) *enforced!* + - [x] [EIP-681](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-681.md) (A standard way of representing various transactions, especially payment requests in Ethers and ERC-20 tokens as URLs) + - [x] [EIP-721](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md) (A standard interface for non-fungible tokens, also known as deeds - ERC-721) + - [x] [EIP-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) (Standard Interface Detection, also known as ERC-165) + - [x] [EIP-777](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-777.md) (New Advanced Token Standard, also known as ERC-777) + - [x] [EIP-820](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-820.md) (Pseudo-introspection Registry Contract, also known as ERC-820) + - [x] [EIP-888](https://github.com/ethereum/EIPs/issues/888) (MultiDimensional Token Standard, also known as ERC-888) + - [x] [EIP-1400](https://github.com/ethereum/EIPs/issues/1411) (Security Token Standard, also known as ERC-1400) + - [x] [EIP-1410](https://github.com/ethereum/EIPs/issues/1410) (Partially Fungible Token Standard, also known as ERC-1410) + - [x] [EIP-1594](https://github.com/ethereum/EIPs/issues/1594) (Core Security Token Standard, also known as ERC-1594) + - [x] [EIP-1643](https://github.com/ethereum/EIPs/issues/1643) (Document Management Standard, also known as ERC-1643) + - [x] [EIP-1644](https://github.com/ethereum/EIPs/issues/1644) (Controller Token Operation Standard, also known as ERC-1644) + - [x] [EIP-1633](https://github.com/ethereum/EIPs/issues/1634) (Re-Fungible Token, also known as ERC-1633) + - [x] [EIP-721x](https://github.com/loomnetwork/erc721x) (An extension of ERC721 that adds support for multi-fungible tokens and batch transfers, while being fully backward-compatible, also known as ERC-721x) + - [x] [EIP-1155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md) (Multi Token Standard, also known as ERC-1155) + - [x] [EIP-1376](https://github.com/ethereum/EIPs/issues/1376) (Service-Friendly Token, also known as ERC-1376) + - [x] [ST-20](https://github.com/PolymathNetwork/polymath-core) - ST-20 token is an Ethereum-based token implemented on top of the ERC-20 protocol that adds the ability for tokens to control transfers based on specific rules + +- [x] RLP encoding +- [x] Batched requests in concurrent mode +- [x] Base58 encoding scheme +- [x] Formatting to and from Ethereum Units +- [x] Comprehensive Unit and Integration Test Coverage + + +### Future steps - [x] Objective-C - a proxy bridge to build your DApp on Objective-C using web3swift - [x] Complete Documentation (https://web3swift.github.io/web3swift) @@ -309,20 +315,6 @@ func setLocalNode(port: Int = 8545) -> Web3? { - [x] Performance Improvements - [x] More convenient methods for basic namespaces -## Credits - -Alex Vlasov, [@shamatar](https://github.com/shamatar) -Petr Korolev, [@skywinder](https://github.com/skywinder) -Anton Grigorev, [@baldyash](https://github.com/BaldyAsh) - -### Security Disclosure - -If you believe you have identified a security vulnerability with web3swift, you should report it as soon as possible via email to [Alex Vlasov](https://github.com/shamatar) alex.m.vlasov@gmail.com. Please do not post it to a public issue tracker. - -[Matter Labs](https://github.com/orgs/matter-labs/people) are charged with open-sourсe and do not require money for using their web3swift lib. -We want to continue to do everything we can to move the needle forward. -If you use any of our libraries for work, see if your employers would be interested in donating. Any amount you can donate today to help us reach our goal would be greatly appreciated. - ## Contribute - If you **need help**, [open an issue](https://github.com/matter-labs/web3swift/issues). @@ -335,6 +327,15 @@ If you use any of our libraries for work, see if your employers would be interes ![Donate](http://qrcoder.ru/code/?0xe22b8979739d724343bd002f9f432f5990879901&4&0) + +## Security Disclosure + +If you believe you have identified a security vulnerability with web3swift, you should report it as soon as possible via email to [hello@matter-labs.io](mailto:hello@matter-labs.io). Please do not post it to a public issue tracker. + +[Matter Labs](https://github.com/orgs/matter-labs/people) are charged with open-sourсe and do not require money for using their web3swift lib. +We want to continue to do everything we can to move the needle forward. +If you use any of our libraries for work, see if your employers would be interested in donating. Any amount you can donate today to help us reach our goal would be greatly appreciated. + ## License web3swift is available under the Apache License 2.0 license. See the [LICENSE](https://github.com/matter-labs/web3swift/blob/master/LICENSE) for details. diff --git a/Sources/web3swift/Convenience/Decodable+Extensions.swift b/Sources/web3swift/Convenience/Decodable+Extensions.swift new file mode 100644 index 000000000..59f82fb0a --- /dev/null +++ b/Sources/web3swift/Convenience/Decodable+Extensions.swift @@ -0,0 +1,146 @@ +// +// DecodingContainer+AnyCollection.swift +// AnyDecodable +// +// Created by levantAJ on 1/18/19. +// Copyright © 2019 levantAJ. All rights reserved. +// +import Foundation + +struct AnyCodingKey: CodingKey { + var stringValue: String + var intValue: Int? + + init?(stringValue: String) { + self.stringValue = stringValue + } + + init?(intValue: Int) { + self.intValue = intValue + self.stringValue = String(intValue) + } +} + +extension KeyedDecodingContainer { + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: [Any].Type, forKey key: KeyedDecodingContainer.Key) throws -> [Any] { + var values = try nestedUnkeyedContainer(forKey: key) + return try values.decode(type) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: [String: Any].Type, forKey key: KeyedDecodingContainer.Key) throws -> [String: Any] { + let values = try nestedContainer(keyedBy: AnyCodingKey.self, forKey: key) + return try values.decode(type) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent(_ type: [Any].Type, forKey key: KeyedDecodingContainer.Key) throws -> [Any]? { + guard contains(key), + try decodeNil(forKey: key) == false else { return nil } + return try decode(type, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent(_ type: [String: Any].Type, forKey key: KeyedDecodingContainer.Key) throws -> [String: Any]? { + guard contains(key), + try decodeNil(forKey: key) == false else { return nil } + return try decode(type, forKey: key) + } +} + +private extension KeyedDecodingContainer { + func decode(_ type: [String: Any].Type) throws -> [String: Any] { + var dictionary: [String: Any] = [:] + for key in allKeys { + if try decodeNil(forKey: key) { + dictionary[key.stringValue] = NSNull() + } else if let bool = try? decode(Bool.self, forKey: key) { + dictionary[key.stringValue] = bool + } else if let string = try? decode(String.self, forKey: key) { + dictionary[key.stringValue] = string + } else if let int = try? decode(Int.self, forKey: key) { + dictionary[key.stringValue] = int + } else if let double = try? decode(Double.self, forKey: key) { + dictionary[key.stringValue] = double + } else if let dict = try? decode([String: Any].self, forKey: key) { + dictionary[key.stringValue] = dict + } else if let array = try? decode([Any].self, forKey: key) { + dictionary[key.stringValue] = array + } + } + return dictionary + } +} + +private extension UnkeyedDecodingContainer { + mutating func decode(_ type: [Any].Type) throws -> [Any] { + var elements: [Any] = [] + while !isAtEnd { + if try decodeNil() { + elements.append(NSNull()) + } else if let int = try? decode(Int.self) { + elements.append(int) + } else if let bool = try? decode(Bool.self) { + elements.append(bool) + } else if let double = try? decode(Double.self) { + elements.append(double) + } else if let string = try? decode(String.self) { + elements.append(string) + } else if let values = try? nestedContainer(keyedBy: AnyCodingKey.self), + let element = try? values.decode([String: Any].self) { + elements.append(element) + } else if var values = try? nestedUnkeyedContainer(), + let element = try? values.decode([Any].self) { + elements.append(element) + } + } + return elements + } +} diff --git a/Sources/web3swift/Convenience/Encodable+Extensions.swift b/Sources/web3swift/Convenience/Encodable+Extensions.swift new file mode 100644 index 000000000..84bad03eb --- /dev/null +++ b/Sources/web3swift/Convenience/Encodable+Extensions.swift @@ -0,0 +1,130 @@ +// +// EncodingContainer+AnyCollection.swift +// AnyDecodable +// +// Created by ShopBack on 1/19/19. +// Copyright © 2019 levantAJ. All rights reserved. +// +import Foundation + +extension KeyedEncodingContainer { + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: [String: Any], forKey key: KeyedEncodingContainer.Key) throws { + var container = nestedContainer(keyedBy: AnyCodingKey.self, forKey: key) + try container.encode(value) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: [Any], forKey key: KeyedEncodingContainer.Key) throws { + var container = nestedUnkeyedContainer(forKey: key) + try container.encode(value) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent(_ value: [String: Any]?, forKey key: KeyedEncodingContainer.Key) throws { + if let value = value { + var container = nestedContainer(keyedBy: AnyCodingKey.self, forKey: key) + try container.encode(value) + } else { + try encodeNil(forKey: key) + } + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent(_ value: [Any]?, forKey key: KeyedEncodingContainer.Key) throws { + if let value = value { + var container = nestedUnkeyedContainer(forKey: key) + try container.encode(value) + } else { + try encodeNil(forKey: key) + } + } +} + +private extension KeyedEncodingContainer where K == AnyCodingKey { + mutating func encode(_ value: [String: Any]) throws { + for (k, v) in value { + let key = AnyCodingKey(stringValue: k)! + switch v { + case is NSNull: + try encodeNil(forKey: key) + case let string as String: + try encode(string, forKey: key) + case let int as Int: + try encode(int, forKey: key) + case let bool as Bool: + try encode(bool, forKey: key) + case let double as Double: + try encode(double, forKey: key) + case let dict as [String: Any]: + try encode(dict, forKey: key) + case let array as [Any]: + try encode(array, forKey: key) + default: + debugPrint("⚠️ Unsuported type!", v) + continue + } + } + } +} + +private extension UnkeyedEncodingContainer { + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: [Any]) throws { + for v in value { + switch v { + case is NSNull: + try encodeNil() + case let string as String: + try encode(string) + case let int as Int: + try encode(int) + case let bool as Bool: + try encode(bool) + case let double as Double: + try encode(double) + case let dict as [String: Any]: + try encode(dict) + case let array as [Any]: + var values = nestedUnkeyedContainer() + try values.encode(array) + default: + debugPrint("⚠️ Unsuported type!", v) + } + } + } + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: [String: Any]) throws { + var container = self.nestedContainer(keyedBy: AnyCodingKey.self) + try container.encode(value) + } +} diff --git a/Sources/web3swift/Convenience/SECP256k1.swift b/Sources/web3swift/Convenience/SECP256k1.swift index b8790a001..800a44765 100755 --- a/Sources/web3swift/Convenience/SECP256k1.swift +++ b/Sources/web3swift/Convenience/SECP256k1.swift @@ -179,7 +179,14 @@ extension SECP256K1 { guard signature.count == 65 else {return nil} var recoverableSignature: secp256k1_ecdsa_recoverable_signature = secp256k1_ecdsa_recoverable_signature() let serializedSignature = Data(signature[0..<64]) - let v = Int32(signature[64]) + var v = Int32(signature[64]) + if v >= 27 && v <= 30 { + v -= 27 + } else if v >= 31 && v <= 34 { + v -= 31 + } else if v >= 35 && v <= 38 { + v -= 35 + } let result = serializedSignature.withUnsafeBytes { (serRawBufferPtr: UnsafeRawBufferPointer) -> Int32? in if let serRawPtr = serRawBufferPtr.baseAddress, serRawBufferPtr.count > 0 { let serPtr = serRawPtr.assumingMemoryBound(to: UInt8.self) @@ -216,10 +223,10 @@ extension SECP256K1 { guard let res = result, res != 0 else { return nil } - if (v == 0) { - serializedSignature.append(0x00) - } else if (v == 1) { - serializedSignature.append(0x01) + if (v == 0 || v == 27 || v == 31 || v == 35) { + serializedSignature.append(0x1b) + } else if (v == 1 || v == 28 || v == 32 || v == 36) { + serializedSignature.append(0x1c) } else { return nil } diff --git a/Sources/web3swift/EthereumABI/ABIDecoding.swift b/Sources/web3swift/EthereumABI/ABIDecoding.swift index 94f953b57..eae5c332b 100755 --- a/Sources/web3swift/EthereumABI/ABIDecoding.swift +++ b/Sources/web3swift/EthereumABI/ABIDecoding.swift @@ -67,9 +67,15 @@ extension ABIDecoder { let dataSlice = elementItself[0 ..< 32] let v = BigUInt(dataSlice) // print("Address element is: \n" + String(v)) - if v == BigUInt(1) { + if v == BigUInt(36) || + v == BigUInt(32) || + v == BigUInt(28) || + v == BigUInt(1) { return (true as AnyObject, type.memoryUsage) - } else if (v == BigUInt(0)) { + } else if v == BigUInt(35) || + v == BigUInt(31) || + v == BigUInt(27) || + v == BigUInt(0) { return (false as AnyObject, type.memoryUsage) } case .bytes(let length): diff --git a/Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift b/Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift index e33ba49e8..ed1576a3b 100755 --- a/Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift +++ b/Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift @@ -58,7 +58,14 @@ extension web3.BrowserFunctions { if signature.count != 65 { return nil} let rData = signature[0..<32].bytes let sData = signature[32..<64].bytes - let vData = signature[64] + var vData = signature[64] + if vData >= 27 && vData <= 30 { + vData -= 27 + } else if vData >= 31 && vData <= 34 { + vData -= 31 + } else if vData >= 35 && vData <= 38 { + vData -= 35 + } guard let signatureData = SECP256K1.marshalSignature(v: vData, r: rData, s: sData) else {return nil} guard let hash = Web3.Utils.hashPersonalMessage(personalMessage) else {return nil} guard let publicKey = SECP256K1.recoverPublicKey(hash: hash, signature: signatureData) else {return nil} diff --git a/Sources/web3swift/Transaction/EthereumTransaction.swift b/Sources/web3swift/Transaction/EthereumTransaction.swift index baadf3330..18dd82d45 100755 --- a/Sources/web3swift/Transaction/EthereumTransaction.swift +++ b/Sources/web3swift/Transaction/EthereumTransaction.swift @@ -112,14 +112,22 @@ public struct EthereumTransaction: CustomStringConvertible { if (self.r == BigUInt(0) && self.s == BigUInt(0)) { return nil } - var normalizedV:BigUInt = BigUInt(0) + var normalizedV:BigUInt = BigUInt(27) let inferedChainID = self.inferedChainID + var d = BigUInt(0) + if self.v >= 35 && self.v <= 38 { + d = BigUInt(35) + } else if self.v >= 31 && self.v <= 34 { + d = BigUInt(31) + } else if self.v >= 27 && self.v <= 30 { + d = BigUInt(27) + } if (self.chainID != nil && self.chainID != BigUInt(0)) { - normalizedV = self.v - BigUInt(35) - self.chainID! - self.chainID! + normalizedV = self.v - d - self.chainID! - self.chainID! } else if (inferedChainID != nil) { - normalizedV = self.v - BigUInt(35) - inferedChainID! - inferedChainID! + normalizedV = self.v - d - inferedChainID! - inferedChainID! } else { - normalizedV = self.v - BigUInt(27) + normalizedV = self.v - d } guard let vData = normalizedV.serialize().setLengthLeft(1) else {return nil} guard let rData = r.serialize().setLengthLeft(32) else {return nil} diff --git a/Sources/web3swift/Transaction/TransactionSigner.swift b/Sources/web3swift/Transaction/TransactionSigner.swift index 3dedb0272..52ab27be9 100755 --- a/Sources/web3swift/Transaction/TransactionSigner.swift +++ b/Sources/web3swift/Transaction/TransactionSigner.swift @@ -52,7 +52,15 @@ public struct Web3Signer { return false } let originalPublicKey = SECP256K1.privateToPublic(privateKey: privateKey) - transaction.v = BigUInt(unmarshalledSignature.v) + BigUInt(35) + chainID + chainID + var d = BigUInt(0) + if unmarshalledSignature.v >= 0 && unmarshalledSignature.v <= 3 { + d = BigUInt(35) + } else if unmarshalledSignature.v >= 27 && unmarshalledSignature.v <= 30 { + d = BigUInt(8) + } else if unmarshalledSignature.v >= 31 && unmarshalledSignature.v <= 34 { + d = BigUInt(4) + } + transaction.v = BigUInt(unmarshalledSignature.v) + d + chainID + chainID transaction.r = BigUInt(Data(unmarshalledSignature.r)) transaction.s = BigUInt(Data(unmarshalledSignature.s)) let recoveredPublicKey = transaction.recoverPublicKey() @@ -83,7 +91,16 @@ public struct Web3Signer { } let originalPublicKey = SECP256K1.privateToPublic(privateKey: privateKey) transaction.chainID = nil - transaction.v = BigUInt(unmarshalledSignature.v) + BigUInt(27) + var d = BigUInt(0) + var a = BigUInt(0) + if unmarshalledSignature.v >= 0 && unmarshalledSignature.v <= 3 { + d = BigUInt(27) + } else if unmarshalledSignature.v >= 31 && unmarshalledSignature.v <= 34 { + a = BigUInt(4) + } else if unmarshalledSignature.v >= 35 && unmarshalledSignature.v <= 38 { + a = BigUInt(8) + } + transaction.v = BigUInt(unmarshalledSignature.v) + d - a transaction.r = BigUInt(Data(unmarshalledSignature.r)) transaction.s = BigUInt(Data(unmarshalledSignature.s)) let recoveredPublicKey = transaction.recoverPublicKey() diff --git a/Sources/web3swift/Utils/ENS/ENSBaseRegistrar.swift b/Sources/web3swift/Utils/ENS/ENSBaseRegistrar.swift index d821c3d15..4c1c89879 100644 --- a/Sources/web3swift/Utils/ENS/ENSBaseRegistrar.swift +++ b/Sources/web3swift/Utils/ENS/ENSBaseRegistrar.swift @@ -56,10 +56,10 @@ public extension ENS { return transaction } - public func getNameExpirity(name: BigUInt) throws -> UInt { + public func getNameExpirity(name: BigUInt) throws -> BigUInt { guard let transaction = self.contract.read("nameExpires", parameters: [name as AnyObject], extraData: Data(), transactionOptions: defaultOptions) else {throw Web3Error.transactionSerializationError} guard let result = try? transaction.call(transactionOptions: defaultOptions) else {throw Web3Error.processingError(desc: "Can't call transaction")} - guard let expirity = result["0"] as? UInt else {throw Web3Error.processingError(desc: "Can't get answer")} + guard let expirity = result["0"] as? BigUInt else {throw Web3Error.processingError(desc: "Can't get answer")} return expirity } diff --git a/Sources/web3swift/Utils/ENS/ENSResolver.swift b/Sources/web3swift/Utils/ENS/ENSResolver.swift index 6cee1177a..67176af8d 100755 --- a/Sources/web3swift/Utils/ENS/ENSResolver.swift +++ b/Sources/web3swift/Utils/ENS/ENSResolver.swift @@ -19,7 +19,7 @@ public extension ENS { case CBOR = 4 case URI = 8 } - + public enum InterfaceName { case addr case name @@ -72,6 +72,14 @@ public extension ENS { guard let supports = result["0"] as? Bool else {throw Web3Error.processingError(desc: "Can't get answer")} return supports } + + public func interfaceImplementer(forNode node: String, interfaceID: String) throws -> EthereumAddress { + guard let nameHash = NameHash.nameHash(node) else {throw Web3Error.processingError(desc: "Failed to get name hash")} + guard let transaction = self.resolverContract.read("interfaceImplementer", parameters: [nameHash, interfaceID] as [AnyObject], extraData: Data(), transactionOptions: defaultOptions) else {throw Web3Error.transactionSerializationError} + guard let result = try? transaction.call(transactionOptions: defaultOptions) else {throw Web3Error.processingError(desc: "Can't call transaction")} + guard let address = result["0"] as? EthereumAddress else {throw Web3Error.processingError(desc: "Can't get address")} + return address + } public func getAddress(forNode node: String) throws -> EthereumAddress { guard let nameHash = NameHash.nameHash(node) else {throw Web3Error.processingError(desc: "Failed to get name hash")} diff --git a/Sources/web3swift/Utils/ENS/ETHRegistrarController.swift b/Sources/web3swift/Utils/ENS/ETHRegistrarController.swift index 4455da8f3..88c8a7f83 100644 --- a/Sources/web3swift/Utils/ENS/ETHRegistrarController.swift +++ b/Sources/web3swift/Utils/ENS/ETHRegistrarController.swift @@ -8,7 +8,6 @@ import Foundation import BigInt -//import EthereumAddress public extension ENS { class ETHRegistrarController { @@ -30,10 +29,10 @@ public extension ENS { self.address = address } - public func getRentPrice(name: String, duration: UInt) throws -> UInt { + public func getRentPrice(name: String, duration: UInt) throws -> BigUInt { guard let transaction = self.contract.read("rentPrice", parameters: [name, duration] as [AnyObject], extraData: Data(), transactionOptions: defaultOptions) else {throw Web3Error.transactionSerializationError} guard let result = try? transaction.call(transactionOptions: defaultOptions) else {throw Web3Error.processingError(desc: "Can't call transaction")} - guard let price = result["0"] as? UInt else {throw Web3Error.processingError(desc: "Can't get answer")} + guard let price = result["0"] as? BigUInt else {throw Web3Error.processingError(desc: "Can't get answer")} return price } @@ -51,26 +50,26 @@ public extension ENS { return available } - public func calculateCommitmentHash(name: String, owner: EthereumAddress, secret: [UInt32]) throws -> [UInt32] { - guard let transaction = self.contract.read("makeCommitment", parameters: [name, owner, secret] as [AnyObject], extraData: Data(), transactionOptions: defaultOptions) else {throw Web3Error.transactionSerializationError} + public func calculateCommitmentHash(name: String, owner: EthereumAddress, secret: String) throws -> Data { + guard let transaction = self.contract.read("makeCommitment", parameters: [name, owner.address, secret] as [AnyObject], extraData: Data(), transactionOptions: defaultOptions) else {throw Web3Error.transactionSerializationError} guard let result = try? transaction.call(transactionOptions: defaultOptions) else {throw Web3Error.processingError(desc: "Can't call transaction")} - guard let hash = result["0"] as? [UInt32] else {throw Web3Error.processingError(desc: "Can't get answer")} + guard let hash = result["0"] as? Data else {throw Web3Error.processingError(desc: "Can't get answer")} return hash } - public func sumbitCommitment(from: EthereumAddress, commitment: [UInt32]) throws -> WriteTransaction { + public func sumbitCommitment(from: EthereumAddress, commitment: Data) throws -> WriteTransaction { defaultOptions.from = from defaultOptions.to = self.address guard let transaction = self.contract.write("commit", parameters: [commitment as AnyObject], extraData: Data(), transactionOptions: defaultOptions) else {throw Web3Error.transactionSerializationError} return transaction } - public func registerName(from: EthereumAddress, name: String, owner: EthereumAddress, duration: UInt32, secret: [UInt32], price: String) throws -> WriteTransaction { + public func registerName(from: EthereumAddress, name: String, owner: EthereumAddress, duration: UInt, secret: String, price: String) throws -> WriteTransaction { guard let amount = Web3.Utils.parseToBigUInt(price, units: .eth) else {throw Web3Error.inputError(desc: "Wrong price: no way for parsing to ether units")} defaultOptions.value = amount defaultOptions.from = from defaultOptions.to = self.address - guard let transaction = self.contract.write("register", parameters: [name, owner, duration, secret] as [AnyObject], extraData: Data(), transactionOptions: defaultOptions) else {throw Web3Error.transactionSerializationError} + guard let transaction = self.contract.write("register", parameters: [name, owner.address, duration, secret] as [AnyObject], extraData: Data(), transactionOptions: defaultOptions) else {throw Web3Error.transactionSerializationError} return transaction } diff --git a/Sources/web3swift/Web3/Web3+Constants.swift b/Sources/web3swift/Web3/Web3+Constants.swift new file mode 100644 index 000000000..3e2f23e04 --- /dev/null +++ b/Sources/web3swift/Web3/Web3+Constants.swift @@ -0,0 +1,15 @@ +// +// Web3+Constants.swift +// web3swift +// +// Created by Anton on 24/06/2019. +// Copyright © 2019 Bankex Foundation. All rights reserved. +// + +import Foundation + +struct Constants { + static let infuraHttpScheme = ".infura.io/v3/" + static let infuraWsScheme = ".infura.io/ws/v3/" + static let infuraToken = "4406c3acf862426c83991f1752c46dd8" +} diff --git a/Sources/web3swift/Web3/Web3+Eth+Websocket.swift b/Sources/web3swift/Web3/Web3+Eth+Websocket.swift index 73901a777..0c1976de3 100644 --- a/Sources/web3swift/Web3/Web3+Eth+Websocket.swift +++ b/Sources/web3swift/Web3/Web3+Eth+Websocket.swift @@ -31,7 +31,7 @@ extension web3.Eth { public func getLatestPendingTransactions(forDelegate delegate: Web3SocketDelegate) throws { let provider = try getWebsocketProvider(forDelegate: delegate) - try provider.filter(method: .newPendingTransactionFilter) + try provider.setFilterAndGetChanges(method: .newPendingTransactionFilter) } public func subscribeOnPendingTransactions(forDelegate delegate: Web3SocketDelegate) throws { diff --git a/Sources/web3swift/Web3/Web3+InfuraProviders.swift b/Sources/web3swift/Web3/Web3+InfuraProviders.swift index 7bdbd6b59..a3a26ace4 100755 --- a/Sources/web3swift/Web3/Web3+InfuraProviders.swift +++ b/Sources/web3swift/Web3/Web3+InfuraProviders.swift @@ -7,13 +7,31 @@ import Foundation import BigInt import Starscream +public enum BlockNumber { + case pending + case latest + case earliest + case exact(BigUInt) + + public var stringValue: String { + switch self { + case .pending: + return "pending" + case .latest: + return "latest" + case .earliest: + return "earliest" + case .exact(let number): + return String(number, radix: 16).addHexPrefix() + } + } +} + /// Custom Web3 HTTP provider of Infura nodes. public final class InfuraProvider: Web3HttpProvider { public init?(_ net:Networks, accessToken token: String? = nil, keystoreManager manager: KeystoreManager? = nil) { - var requestURLstring = "https://" + net.name + ".infura.io/" - if token != nil { - requestURLstring = requestURLstring + token! - } + var requestURLstring = "https://" + net.name + Constants.infuraHttpScheme + requestURLstring += token != nil ? token! : Constants.infuraToken let providerURL = URL(string: requestURLstring) super.init(providerURL!, network: net, keystoreManager: manager) } @@ -28,43 +46,51 @@ public final class InfuraWebsocketProvider: WebsocketProvider { public init?(_ network: Networks, delegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil) { guard network == Networks.Kovan || network == Networks.Rinkeby || network == Networks.Ropsten || network == Networks.Mainnet else {return nil} let networkName = network.name - let urlString = "wss://\(networkName).infura.io/ws" + let urlString = "wss://" + networkName + Constants.infuraWsScheme guard URL(string: urlString) != nil else {return nil} super.init(urlString, delegate: delegate, + projectId: projectId, keystoreManager: manager, network: network) } public init?(_ endpoint: String, delegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil) { guard URL(string: endpoint) != nil else {return nil} super.init(endpoint, delegate: delegate, + projectId: projectId, keystoreManager: manager) } public init?(_ endpoint: URL, delegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil) { super.init(endpoint, delegate: delegate, + projectId: projectId, keystoreManager: manager) } override public class func connectToSocket(_ endpoint: String, delegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil, network net: Networks? = nil) -> WebsocketProvider? { guard let socketProvider = InfuraWebsocketProvider(endpoint, delegate: delegate, + projectId: projectId, keystoreManager: manager) else {return nil} socketProvider.connectSocket() return socketProvider @@ -72,10 +98,12 @@ public final class InfuraWebsocketProvider: WebsocketProvider { override public class func connectToSocket(_ endpoint: URL, delegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil, network net: Networks? = nil) -> WebsocketProvider? { guard let socketProvider = InfuraWebsocketProvider(endpoint, delegate: delegate, + projectId: projectId, keystoreManager: manager) else {return nil} socketProvider.connectSocket() return socketProvider @@ -83,9 +111,11 @@ public final class InfuraWebsocketProvider: WebsocketProvider { public static func connectToInfuraSocket(_ network: Networks, delegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil) -> InfuraWebsocketProvider? { guard let socketProvider = InfuraWebsocketProvider(network, delegate: delegate, + projectId: projectId, keystoreManager: manager) else {return nil} socketProvider.connectSocket() return socketProvider @@ -95,10 +125,11 @@ public final class InfuraWebsocketProvider: WebsocketProvider { let request = JSONRPCRequestFabric.prepareRequest(method, parameters: params) let encoder = JSONEncoder() let requestData = try encoder.encode(request) + print(String(decoding: requestData, as: UTF8.self)) writeMessage(requestData) } - public func filter(method: InfuraWebsocketMethod, params: [Encodable]? = nil) throws { + public func setFilterAndGetChanges(method: InfuraWebsocketMethod, params: [Encodable]? = nil) throws { filterTimer?.invalidate() filterID = nil let params = params ?? [] @@ -110,25 +141,59 @@ public final class InfuraWebsocketProvider: WebsocketProvider { filterTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(getFilterChanges), userInfo: nil, repeats: true) } + public func setFilterAndGetChanges(method: InfuraWebsocketMethod, address: EthereumAddress? = nil, fromBlock: BlockNumber? = nil, toBlock: BlockNumber? = nil, topics: [String]? = nil) throws { + let filterParams = EventFilterParameters(fromBlock: fromBlock?.stringValue, toBlock: toBlock?.stringValue, topics: [topics], address: [address?.address]) + try setFilterAndGetChanges(method: method, params: [filterParams]) + } + + public func setFilterAndGetLogs(method: InfuraWebsocketMethod, params: [Encodable]? = nil) throws { + filterTimer?.invalidate() + filterID = nil + let params = params ?? [] + let paramsCount = params.count + guard method.requiredNumOfParameters == paramsCount || method.requiredNumOfParameters == nil else { + throw Web3Error.inputError(desc: "Wrong number of params: need - \(method.requiredNumOfParameters!), got - \(paramsCount)") + } + try writeMessage(method: method, params: params) + filterTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(getFilterLogs), userInfo: nil, repeats: true) + } + + public func setFilterAndGetLogs(method: InfuraWebsocketMethod, address: EthereumAddress? = nil, fromBlock: BlockNumber? = nil, toBlock: BlockNumber? = nil, topics: [String]? = nil) throws { + let filterParams = EventFilterParameters(fromBlock: fromBlock?.stringValue, toBlock: toBlock?.stringValue, topics: [topics], address: [address?.address]) + try setFilterAndGetLogs(method: method, params: [filterParams]) + } + @objc public func getFilterChanges() throws { - if let id = self.filterID { + if let id = filterID { filterTimer?.invalidate() let method = InfuraWebsocketMethod.getFilterChanges try writeMessage(method: method, params: [id]) } } - public func getFilterLogs() throws { - if let id = self.filterID { + @objc public func getFilterLogs() throws { + if let id = filterID { + filterTimer?.invalidate() let method = InfuraWebsocketMethod.getFilterLogs try writeMessage(method: method, params: [id]) } } + public func getFilterLogs(address: EthereumAddress? = nil, fromBlock: BlockNumber? = nil, toBlock: BlockNumber? = nil, topics: [String]? = nil) throws { + if let id = filterID { + let filterParams = EventFilterParameters(fromBlock: fromBlock?.stringValue, toBlock: toBlock?.stringValue, topics: [topics], address: [address?.address]) + let method = InfuraWebsocketMethod.getFilterLogs + try writeMessage(method: method, params: [id, filterParams]) + } + } + public func unistallFilter() throws { - if let id = self.filterID { + if let id = filterID { + filterID = nil let method = InfuraWebsocketMethod.uninstallFilter try writeMessage(method: method, params: [id]) + } else { + throw Web3Error.nodeError(desc: "No filter set") } } @@ -149,6 +214,19 @@ public final class InfuraWebsocketProvider: WebsocketProvider { try writeMessage(method: method, params: params) } + public func subscribeOnLogs(addresses: [EthereumAddress]? = nil, topics: [String]? = nil) throws { + let method = InfuraWebsocketMethod.subscribe + var stringAddresses = [String]() + if let addrs = addresses { + for addr in addrs { + stringAddresses.append(addr.address) + } + } +// let ts = topics == nil ? nil : [topics!] + let filterParams = EventFilterParameters(fromBlock: nil, toBlock: nil, topics: [topics], address: stringAddresses) + try writeMessage(method: method, params: ["logs", filterParams]) + } + public func subscribeOnNewPendingTransactions() throws { let method = InfuraWebsocketMethod.subscribe let params = ["newPendingTransactions"] diff --git a/Sources/web3swift/Web3/Web3+Utils.swift b/Sources/web3swift/Web3/Web3+Utils.swift index 5c079f1f4..82ca74a74 100755 --- a/Sources/web3swift/Web3/Web3+Utils.swift +++ b/Sources/web3swift/Web3/Web3+Utils.swift @@ -4887,7 +4887,7 @@ extension Web3.Utils { """ //function setAddr(bytes32 node, address addr) - public static var resolverABI = """ + public static var legacyResolverABI = """ [ { "constant": true, @@ -5336,6 +5336,561 @@ extension Web3.Utils { "type": "event" } ] +""" + + public static var resolverABI = """ +[ + { + "constant": true, + "inputs": + [ + { + "name": "interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": + [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "key", + "type": "string" + }, + { + "name": "value", + "type": "string" + } + ], + "name": "setText", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "interfaceID", + "type": "bytes4" + } + ], + "name": "interfaceImplementer", + "outputs": + [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "contentTypes", + "type": "uint256" + } + ], + "name": "ABI", + "outputs": + [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "x", + "type": "bytes32" + }, + { + "name": "y", + "type": "bytes32" + } + ], + "name": "setPubkey", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "hash", + "type": "bytes" + } + ], + "name": "setContenthash", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": + [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "key", + "type": "string" + } + ], + "name": "text", + "outputs": + [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "contentType", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "name": "setABI", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "name", + "outputs": + [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "name", + "type": "string" + } + ], + "name": "setName", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": + [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": + [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "contenthash", + "outputs": + [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "pubkey", + "outputs": + [ + { + "name": "x", + "type": "bytes32" + }, + { + "name": "y", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "addr", + "type": "address" + } + ], + "name": "setAddr", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": + [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "interfaceID", + "type": "bytes4" + }, + { + "name": "implementer", + "type": "address" + } + ], + "name": "setInterface", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": + [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "indexedKey", + "type": "string" + }, + { + "indexed": false, + "name": "key", + "type": "string" + } + ], + "name": "TextChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "x", + "type": "bytes32" + }, + { + "indexed": false, + "name": "y", + "type": "bytes32" + } + ], + "name": "PubkeyChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "name", + "type": "string" + } + ], + "name": "NameChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "name": "interfaceID", + "type": "bytes4" + }, + { + "indexed": false, + "name": "implementer", + "type": "address" + } + ], + "name": "InterfaceChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "hash", + "type": "bytes" + } + ], + "name": "ContenthashChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "a", + "type": "address" + } + ], + "name": "AddrChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "name": "contentType", + "type": "uint256" + } + ], + "name": "ABIChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": true, + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + } +] """ } @@ -5558,7 +6113,15 @@ extension Web3.Utils { if signature.count != 65 { return nil} let rData = signature[0..<32].bytes let sData = signature[32..<64].bytes - let vData = signature[64] + var vData = signature[64] + if vData >= 27 && vData <= 30 { + vData -= 27 + } else if vData >= 31 && vData <= 34 { + vData -= 31 + } else if vData >= 35 && vData <= 38 { + vData -= 35 + } + guard let signatureData = SECP256K1.marshalSignature(v: vData, r: rData, s: sData) else {return nil} guard let hash = Web3.Utils.hashPersonalMessage(personalMessage) else {return nil} guard let publicKey = SECP256K1.recoverPublicKey(hash: hash, signature: signatureData) else {return nil} @@ -5574,7 +6137,14 @@ extension Web3.Utils { if signature.count != 65 { return nil} let rData = signature[0..<32].bytes let sData = signature[32..<64].bytes - let vData = signature[64] + var vData = signature[64] + if vData >= 27 && vData <= 30 { + vData -= 27 + } else if vData >= 31 && vData <= 34 { + vData -= 31 + } else if vData >= 35 && vData <= 38 { + vData -= 35 + } guard let signatureData = SECP256K1.marshalSignature(v: vData, r: rData, s: sData) else {return nil} guard let publicKey = SECP256K1.recoverPublicKey(hash: hash, signature: signatureData) else {return nil} return Web3.Utils.publicToAddress(publicKey) diff --git a/Sources/web3swift/Web3/Web3+WebsocketProvider.swift b/Sources/web3swift/Web3/Web3+WebsocketProvider.swift index 6904a1115..e25122ace 100644 --- a/Sources/web3swift/Web3/Web3+WebsocketProvider.swift +++ b/Sources/web3swift/Web3/Web3+WebsocketProvider.swift @@ -15,8 +15,7 @@ public protocol IWebsocketProvider { var delegate: Web3SocketDelegate {get set} func connectSocket() throws func disconnectSocket() throws - func writeMessage(_ string: String) - func writeMessage(_ data: Data) + func writeMessage(_ message: T) } public enum InfuraWebsocketMethod: String, Encodable { @@ -113,23 +112,25 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg public var socket: WebSocket public var delegate: Web3SocketDelegate + private var websocketConnected: Bool = false + private var writeTimer: Timer? = nil + private var messagesStringToWrite: [String] = [] + private var messagesDataToWrite: [Data] = [] + public init?(_ endpoint: URL, delegate wsdelegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil, network net: Networks? = nil) { - delegate = wsdelegate - attachedKeystoreManager = manager - url = endpoint - socket = WebSocket(url: endpoint) - socket.delegate = self - let endpointString = endpoint.absoluteString + websocketConnected = false + var endpointString = endpoint.absoluteString if !(endpointString.hasPrefix("wss://") || endpointString.hasPrefix("ws://")) { return nil } - if net == nil { - if endpointString.hasPrefix("wss://") && endpointString.hasSuffix(".infura.io/ws") { + if endpointString.hasPrefix("wss://") && endpointString.hasSuffix(Constants.infuraWsScheme) { + if net == nil { let networkString = endpointString.replacingOccurrences(of: "wss://", with: "") - .replacingOccurrences(of: ".infura.io/ws", with: "") + .replacingOccurrences(of: Constants.infuraWsScheme, with: "") switch networkString { case "mainnet": network = Networks.Mainnet @@ -142,30 +143,35 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg default: break } + } else { + network = net + } + if network != nil { + endpointString += projectId ?? Constants.infuraToken } - } else { - network = net } + url = URL(string: endpointString)! + delegate = wsdelegate + attachedKeystoreManager = manager + socket = WebSocket(url: url) + socket.delegate = self } public init?(_ endpoint: String, delegate wsdelegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil, network net: Networks? = nil) { - guard let endpointUrl = URL(string: endpoint) else {return nil} - delegate = wsdelegate - attachedKeystoreManager = manager - url = endpointUrl - socket = WebSocket(url: endpointUrl) - socket.delegate = self - let endpointString = endpointUrl.absoluteString - if !(endpointString.hasPrefix("wss://") || endpointString.hasPrefix("ws://")) { + guard URL(string: endpoint) != nil else {return nil} + var finalEndpoint = endpoint + websocketConnected = false + if !(endpoint.hasPrefix("wss://") || endpoint.hasPrefix("ws://")) { return nil } - if net == nil { - if endpointString.hasPrefix("wss://") && endpointString.hasSuffix(".infura.io/ws") { - let networkString = endpointString.replacingOccurrences(of: "wss://", with: "") - .replacingOccurrences(of: ".infura.io/ws", with: "") + if endpoint.hasPrefix("wss://") && endpoint.hasSuffix(Constants.infuraWsScheme) { + if net == nil { + let networkString = endpoint.replacingOccurrences(of: "wss://", with: "") + .replacingOccurrences(of: Constants.infuraWsScheme, with: "") switch networkString { case "mainnet": network = Networks.Mainnet @@ -178,26 +184,42 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg default: break } + } else { + network = net + } + if network != nil { + finalEndpoint += projectId ?? Constants.infuraToken } - } else { - network = net } + url = URL(string: finalEndpoint)! + delegate = wsdelegate + attachedKeystoreManager = manager + socket = WebSocket(url: url) + socket.delegate = self + } + + deinit { + writeTimer?.invalidate() } public func connectSocket() { + writeTimer?.invalidate() socket.connect() } public func disconnectSocket() { + writeTimer?.invalidate() socket.disconnect() } public class func connectToSocket(_ endpoint: String, delegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil, network net: Networks? = nil) -> WebsocketProvider? { guard let socketProvider = WebsocketProvider(endpoint, delegate: delegate, + projectId: projectId, keystoreManager: manager, network: net) else { return nil @@ -208,10 +230,12 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg public class func connectToSocket(_ endpoint: URL, delegate: Web3SocketDelegate, + projectId: String? = nil, keystoreManager manager: KeystoreManager? = nil, network net: Networks? = nil) -> WebsocketProvider? { guard let socketProvider = WebsocketProvider(endpoint, delegate: delegate, + projectId: projectId, keystoreManager: manager, network: net) else { return nil @@ -220,12 +244,34 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg return socketProvider } - public func writeMessage(_ string: String) { - socket.write(string: string) + public func writeMessage(_ message: T) { + var sMessage: String? = nil + var dMessage: Data? = nil + if !(message.self is String) && !(message.self is Data) { + sMessage = "\(message)" + } else if message.self is String { + sMessage = message as? String + } else if message.self is Data { + dMessage = message as? Data + } + if sMessage != nil { + self.messagesStringToWrite.append(sMessage!) + } else if dMessage != nil { + self.messagesDataToWrite.append(dMessage!) + } + writeTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(performWriteOperations), userInfo: nil, repeats: true) } - public func writeMessage(_ data: Data) { - socket.write(data: data) + @objc private func performWriteOperations() { + if websocketConnected { + writeTimer?.invalidate() + for s in messagesStringToWrite { + socket.write(string: s) + } + for d in messagesDataToWrite { + socket.write(data: d) + } + } } public func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { @@ -240,10 +286,12 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg public func websocketDidConnect(socket: WebSocketClient) { print("websocket is connected") + websocketConnected = true } public func websocketDidDisconnect(socket: WebSocketClient, error: Error?) { print("websocket is disconnected with \(error?.localizedDescription ?? "no error")") + websocketConnected = false } public func websocketDidReceivePong(socket: WebSocketClient, data: Data?) { diff --git a/Sources/web3swift/Web3/Web3.swift b/Sources/web3swift/Web3/Web3.swift index f7139bb9a..91ab2d54f 100755 --- a/Sources/web3swift/Web3/Web3.swift +++ b/Sources/web3swift/Web3/Web3.swift @@ -18,7 +18,7 @@ public enum Web3Error: Error { case generalError(err:Error) case unknownError - var errorDescription: String { + public var errorDescription: String { switch self { case .transactionSerializationError: diff --git a/Tests/web3swiftTests/web3swift_Websockets_Tests.swift b/Tests/web3swiftTests/web3swift_Websockets_Tests.swift index 54f04e61d..a59523a40 100644 --- a/Tests/web3swiftTests/web3swift_Websockets_Tests.swift +++ b/Tests/web3swiftTests/web3swift_Websockets_Tests.swift @@ -13,21 +13,26 @@ import Starscream class SpyDelegate: Web3SocketDelegate { // Setting .None is unnecessary, but helps with clarity imho - var somethingWithDelegateResult: String? = nil + var somethingWithDelegateResult: Any? = nil // Async test code needs to fulfill the XCTestExpecation used for the test // when all the async operations have been completed. For this reason we need // to store a reference to the expectation var asyncExpectation: XCTestExpectation? + var fulfilled = false func received(message: Any) { + somethingWithDelegateResult = message guard let expectation = asyncExpectation else { XCTFail("SpyDelegate was not setup correctly. Missing XCTExpectation reference") return } - - print(message as! String) - expectation.fulfill() + print(message) + if !fulfilled { + print("fullfilled") + fulfilled = true + expectation.fulfill() + } } func gotError(error: Error) { @@ -44,19 +49,93 @@ class web3swift_websocket_Tests: XCTestCase { guard let socketProvider = InfuraWebsocketProvider.connectToInfuraSocket(.Mainnet, delegate: spyDelegate) else { return XCTFail() } + self.socketProvider = socketProvider + spyDelegate.asyncExpectation = expectation(description: "Delegate called") + try! self.socketProvider!.subscribeOnNewPendingTransactions() +// DispatchQueue.main.asyncAfter(deadline: .now()+5) { [unowned self] in +// try! self.socketProvider!.subscribeOnNewPendingTransactions() +// } + waitForExpectations(timeout: 1000) { error in + if let error = error { + XCTFail("waitForExpectationsWithTimeout errored: \(error)") + } + + guard self.spyDelegate.somethingWithDelegateResult != nil else { + XCTFail("Expected delegate to be called") + return + } + + XCTAssert(true) + } + } + + func testSubscribeOnLogs() { + guard let socketProvider = InfuraWebsocketProvider.connectToInfuraSocket(.Mainnet, delegate: spyDelegate) else { + return XCTFail() + } + self.socketProvider = socketProvider + spyDelegate.asyncExpectation = expectation(description: "Delegate called") + try! self.socketProvider!.subscribeOnLogs(addresses: [EthereumAddress("0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359")!], topics: nil) + // DispatchQueue.main.asyncAfter(deadline: .now()+5) { [unowned self] in + // try! self.socketProvider!.subscribeOnNewPendingTransactions() + // } + waitForExpectations(timeout: 1000) { error in + if let error = error { + XCTFail("waitForExpectationsWithTimeout errored: \(error)") + } + + guard self.spyDelegate.somethingWithDelegateResult != nil else { + XCTFail("Expected delegate to be called") + return + } + + XCTAssert(true) + } + } + + func testSubscribeOnNewHeads() { + guard let socketProvider = InfuraWebsocketProvider.connectToInfuraSocket(.Mainnet, delegate: spyDelegate) else { + return XCTFail() + } + self.socketProvider = socketProvider + spyDelegate.asyncExpectation = expectation(description: "Delegate called") + try! self.socketProvider!.subscribeOnNewHeads() + // DispatchQueue.main.asyncAfter(deadline: .now()+5) { [unowned self] in + // try! self.socketProvider!.subscribeOnNewPendingTransactions() + // } + waitForExpectations(timeout: 1000) { error in + if let error = error { + XCTFail("waitForExpectationsWithTimeout errored: \(error)") + } + + guard self.spyDelegate.somethingWithDelegateResult != nil else { + XCTFail("Expected delegate to be called") + return + } + + XCTAssert(true) + } + } + + func testFilter() { + guard let socketProvider = InfuraWebsocketProvider.connectToInfuraSocket(.Mainnet, delegate: spyDelegate) else { + return XCTFail() + } + self.socketProvider = socketProvider spyDelegate.asyncExpectation = expectation(description: "Delegate called") - try! socketProvider.filter(method: .newPendingTransactionFilter) - - waitForExpectations(timeout: 100) { error in + try! self.socketProvider?.setFilterAndGetLogs(method: .newFilter, address: EthereumAddress("0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359")!, fromBlock: .earliest, toBlock: .latest, topics: nil) + // DispatchQueue.main.asyncAfter(deadline: .now()+5) { [unowned self] in + // try! self.socketProvider!.subscribeOnNewPendingTransactions() + // } + waitForExpectations(timeout: 1000) { error in if let error = error { XCTFail("waitForExpectationsWithTimeout errored: \(error)") } - guard let result = self.spyDelegate.somethingWithDelegateResult else { + guard self.spyDelegate.somethingWithDelegateResult != nil else { XCTFail("Expected delegate to be called") return } - print(result) XCTAssert(true) } diff --git a/Tests/web3swiftTests/web3swift_keystores_Tests.swift b/Tests/web3swiftTests/web3swift_keystores_Tests.swift index 69820f1d0..e49b8d320 100755 --- a/Tests/web3swiftTests/web3swift_keystores_Tests.swift +++ b/Tests/web3swiftTests/web3swift_keystores_Tests.swift @@ -156,8 +156,8 @@ class web3swift_Keystores_tests: XCTestCase { print(recreatedStore!.addresses![0].address) print(recreatedStore!.addresses![1].address) // This will fail. It wont fail if use scrypt from pod 'scrypt', '2.0', not from CryptoSwift - XCTAssert(keystore?.addresses![0] == recreatedStore?.addresses![0]) - XCTAssert(keystore?.addresses![1] == recreatedStore?.addresses![1]) + XCTAssert(keystore?.addresses![0] == recreatedStore?.addresses![1]) + XCTAssert(keystore?.addresses![1] == recreatedStore?.addresses![0]) } // func testPBKDF2() { diff --git a/Web3swift.pod.podspec b/Web3swift.pod.podspec index 15f3e3488..fcd01b1ed 100755 --- a/Web3swift.pod.podspec +++ b/Web3swift.pod.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'Web3swift.pod' - spec.version = '2.2.0' + spec.version = '2.2.1' spec.ios.deployment_target = "9.0" spec.osx.deployment_target = "10.11" spec.license = { :type => 'Apache License 2.0', :file => 'LICENSE.md' } @@ -13,7 +13,7 @@ Pod::Spec.new do |spec| spec.module_name = 'web3swift' spec.frameworks = 'CoreImage' spec.dependency 'PromiseKit', '~> 6.8.4' - spec.dependency 'BigInt', '~> 3.1' + spec.dependency 'BigInt', '~> 4.0' spec.dependency 'Starscream', '~> 3.1.0' spec.dependency 'CryptoSwift', '~> 1.0.0' spec.dependency 'secp256k1.c', '~> 0.1' diff --git a/web3swift.podspec b/web3swift.podspec index e50634f5a..4dc7f3965 100755 --- a/web3swift.podspec +++ b/web3swift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'web3swift' - spec.version = '2.2.0' + spec.version = '2.2.1' spec.ios.deployment_target = "9.0" spec.osx.deployment_target = "10.11" spec.license = { :type => 'Apache License 2.0', :file => 'LICENSE.md' } @@ -12,7 +12,7 @@ Pod::Spec.new do |spec| spec.swift_version = '5.0' spec.frameworks = 'CoreImage' spec.dependency 'PromiseKit', '~> 6.8.4' - spec.dependency 'BigInt', '~> 3.1' + spec.dependency 'BigInt', '~> 4.0' spec.dependency 'Starscream', '~> 3.1.0' spec.dependency 'CryptoSwift', '~> 1.0.0' spec.dependency 'secp256k1.c', '~> 0.1' diff --git a/web3swift.xcodeproj/project.pbxproj b/web3swift.xcodeproj/project.pbxproj index 6f4274fbc..a3d1aa57e 100755 --- a/web3swift.xcodeproj/project.pbxproj +++ b/web3swift.xcodeproj/project.pbxproj @@ -53,6 +53,8 @@ 13C3392521B6C62400F33F5E /* secp256k1_ec_mult_static_context.h in Headers */ = {isa = PBXBuildFile; fileRef = 13C338F621B6C62400F33F5E /* secp256k1_ec_mult_static_context.h */; }; 13C3392621B6C62400F33F5E /* scratch.h in Headers */ = {isa = PBXBuildFile; fileRef = 13C338F721B6C62400F33F5E /* scratch.h */; }; 13C3392821B6C68900F33F5E /* secp256k1.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13C3388E21B6C2DD00F33F5E /* secp256k1.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 3A7EA35E2280EA9A005120C2 /* Encodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7EA35D2280EA9A005120C2 /* Encodable+Extensions.swift */; }; + 3A7EA3602280EB27005120C2 /* Decodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7EA35F2280EB27005120C2 /* Decodable+Extensions.swift */; }; 3AA8151C2276E42F00F5DB52 /* EventFiltering.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA815172276E42F00F5DB52 /* EventFiltering.swift */; }; 3AA8151D2276E42F00F5DB52 /* ComparisonExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA815182276E42F00F5DB52 /* ComparisonExtensions.swift */; }; 3AA8151E2276E42F00F5DB52 /* EthereumFilterEncodingExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA815192276E42F00F5DB52 /* EthereumFilterEncodingExtensions.swift */; }; @@ -184,6 +186,7 @@ 3AA816372276E48400F5DB52 /* web3swift_Eventloop_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA816222276E48400F5DB52 /* web3swift_Eventloop_Tests.swift */; }; 3AA816382276E48400F5DB52 /* web3swift_Websockets_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA816232276E48400F5DB52 /* web3swift_Websockets_Tests.swift */; }; 3AA8163A2276E4AE00F5DB52 /* SECP256k1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA816392276E4AE00F5DB52 /* SECP256k1.swift */; }; + 3AEF4ABF22C0B6BE00AC7929 /* Web3+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEF4ABE22C0B6BE00AC7929 /* Web3+Constants.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -242,6 +245,8 @@ 13CE02B021FC846800CE7148 /* RELEASE_GUIDE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = RELEASE_GUIDE.md; sourceTree = ""; }; 13CE02B121FC846900CE7148 /* BUILD_GUIDE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = BUILD_GUIDE.md; sourceTree = ""; }; 13CE02B421FC849400CE7148 /* Web3swift.pod.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Web3swift.pod.podspec; sourceTree = ""; }; + 3A7EA35D2280EA9A005120C2 /* Encodable+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Encodable+Extensions.swift"; sourceTree = ""; }; + 3A7EA35F2280EB27005120C2 /* Decodable+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Decodable+Extensions.swift"; sourceTree = ""; }; 3AA815172276E42F00F5DB52 /* EventFiltering.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventFiltering.swift; sourceTree = ""; }; 3AA815182276E42F00F5DB52 /* ComparisonExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComparisonExtensions.swift; sourceTree = ""; }; 3AA815192276E42F00F5DB52 /* EthereumFilterEncodingExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EthereumFilterEncodingExtensions.swift; sourceTree = ""; }; @@ -379,6 +384,7 @@ 3AA8163F2276E5A800F5DB52 /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; }; 3AA816402276E5A800F5DB52 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; 3AA816412276E5A900F5DB52 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 3AEF4ABE22C0B6BE00AC7929 /* Web3+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Web3+Constants.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -693,6 +699,7 @@ 3AA815542276E44100F5DB52 /* Web3+Utils.swift */, 3AA815552276E44100F5DB52 /* Web3+MutatingTransaction.swift */, 3AA815562276E44100F5DB52 /* Web3+JSONRPC.swift */, + 3AEF4ABE22C0B6BE00AC7929 /* Web3+Constants.swift */, 3AA815572276E44100F5DB52 /* Web3+Instance.swift */, 3AA815582276E44100F5DB52 /* Web3+Contract.swift */, 3AA815592276E44100F5DB52 /* Web3+EventParser.swift */, @@ -716,6 +723,8 @@ 3AA815642276E44100F5DB52 /* BigUInt+Extensions.swift */, 3AA815652276E44100F5DB52 /* CryptoExtensions.swift */, 3AA815662276E44100F5DB52 /* String+Extension.swift */, + 3A7EA35D2280EA9A005120C2 /* Encodable+Extensions.swift */, + 3A7EA35F2280EB27005120C2 /* Decodable+Extensions.swift */, 3AA815672276E44100F5DB52 /* NSRegularExpressionExtension.swift */, 3AA815682276E44100F5DB52 /* Dictionary+Extension.swift */, 3AA815692276E44100F5DB52 /* Data+Extension.swift */, @@ -1154,6 +1163,7 @@ buildActionMask = 2147483647; files = ( 3AA815CF2276E44100F5DB52 /* Web3+Eth+Websocket.swift in Sources */, + 3A7EA35E2280EA9A005120C2 /* Encodable+Extensions.swift in Sources */, 3AA815D22276E44100F5DB52 /* Web3+JSONRPC.swift in Sources */, 3AA815F82276E44100F5DB52 /* Promise+Web3+Contract+GetIndexedEvents.swift in Sources */, 3AA815E42276E44100F5DB52 /* Data+Extension.swift in Sources */, @@ -1170,6 +1180,7 @@ 3AA816062276E44100F5DB52 /* Web3+ST20.swift in Sources */, 3AA815FA2276E44100F5DB52 /* Promise+Web3+Eth+GetAccounts.swift in Sources */, 3AA815E72276E44100F5DB52 /* Array+Extension.swift in Sources */, + 3AEF4ABF22C0B6BE00AC7929 /* Web3+Constants.swift in Sources */, 3AA815DD2276E44100F5DB52 /* Web3+Options.swift in Sources */, 3AA815A92276E44100F5DB52 /* ENSReverseRegistrar.swift in Sources */, 3AA815EF2276E44100F5DB52 /* Promise+HttpProvider.swift in Sources */, @@ -1219,6 +1230,7 @@ 3AA815DA2276E44100F5DB52 /* Web3+Eth.swift in Sources */, 3AA816082276E44100F5DB52 /* Web3+ERC1644.swift in Sources */, 3AA815AB2276E44100F5DB52 /* ETHRegistrarController.swift in Sources */, + 3A7EA3602280EB27005120C2 /* Decodable+Extensions.swift in Sources */, 3AA816022276E44100F5DB52 /* Web3+ERC1633.swift in Sources */, 3AA815CB2276E44100F5DB52 /* Web3.swift in Sources */, 3AA815B62276E44100F5DB52 /* BIP39+WordLists.swift in Sources */, @@ -1441,7 +1453,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 8DB897QKJA; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1488,7 +1500,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 8DB897QKJA; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1537,7 +1549,7 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 8DB897QKJA; "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "$(SRCROOT)/Carthage/Build/iOS"; "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(SRCROOT)/Carthage/Build/iOS"; "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = "$(SRCROOT)/Carthage/Build/Mac"; @@ -1569,7 +1581,7 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 8DB897QKJA; "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "$(SRCROOT)/Carthage/Build/iOS"; "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(SRCROOT)/Carthage/Build/iOS"; "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = "$(SRCROOT)/Carthage/Build/Mac"; @@ -1599,10 +1611,11 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DB897QKJA; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1639,6 +1652,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DB897QKJA; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath";