diff --git a/Sources/Core/Contract/ContractProtocol.swift b/Sources/Core/Contract/ContractProtocol.swift index 495b032f7..9302cbc8e 100755 --- a/Sources/Core/Contract/ContractProtocol.swift +++ b/Sources/Core/Contract/ContractProtocol.swift @@ -173,6 +173,11 @@ public protocol ContractProtocol { /// - Returns: `true` if event is possibly present, `false` if definitely not present and `nil` if event with given name /// is not part of the ``EthereumContract/abi``. func testBloomForEventPresence(eventName: String, bloom: EthereumBloomFilter) -> Bool? + + /// Given the transaction data searches for a match in ``ContractProtocol/methods``. + /// - Parameter data: encoded function call used in transaction data field. Must be at least 4 bytes long. + /// - Returns: function decoded from the ABI of this contract or `nil` if nothing was found. + func getFunctionCalled(_ data: Data) -> ABI.Element.Function? } // MARK: - Overloaded ContractProtocol's functions @@ -333,4 +338,9 @@ extension DefaultContractProtocol { guard let function = methods[methodSignature]?.first else { return nil } return function.decodeInputData(Data(data[4 ..< data.count])) } + + public func getFunctionCalled(_ data: Data) -> ABI.Element.Function? { + guard data.count >= 4 else { return nil } + return methods[data[0..<4].toHexString().addHexPrefix()]?.first + } } diff --git a/Sources/Core/Web3Error/Web3Error.swift b/Sources/Core/Web3Error/Web3Error.swift index 251bcb069..bb21193dc 100644 --- a/Sources/Core/Web3Error/Web3Error.swift +++ b/Sources/Core/Web3Error/Web3Error.swift @@ -54,8 +54,8 @@ public enum Web3Error: LocalizedError { return "Server error: \(code)" case let .clientError(code: code): return "Client error: \(code)" - case .valueError: - return "You're passing value that doesn't supported by this method." + case .valueError(let errorDescription): + return (errorDescription?.isEmpty ?? true) ? "You're passing value that isn't supported by this method" : errorDescription! } } } diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+Call.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+Call.swift deleted file mode 100755 index c837c1ea0..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+Call.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import Core - -extension Web3.Eth { - public func callTransaction(_ transaction: CodableTransaction) async throws -> Data { - let request = APIRequest.call(transaction, transaction.callOnBlock ?? .latest) - return try await APIRequest.sendRequest(with: provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+EstimateGas.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+EstimateGas.swift deleted file mode 100755 index 7d1fa7578..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+EstimateGas.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func estimateGas(for transaction: CodableTransaction, onBlock: BlockNumber = .latest) async throws -> BigUInt { - let request = APIRequest.estimateGas(transaction, onBlock) - return try await APIRequest.sendRequest(with: provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+FeeHistory.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+FeeHistory.swift deleted file mode 100644 index d290140fc..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+FeeHistory.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - func feeHistory(blockCount: BigUInt, block: BlockNumber, percentiles: [Double]) async throws -> Oracle.FeeHistory { - let request = APIRequest.feeHistory(blockCount, block, percentiles) - return try await APIRequest.sendRequest(with: web3.provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetAccounts.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetAccounts.swift deleted file mode 100755 index f433fabe2..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetAccounts.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func ownedAccounts() async throws -> [EthereumAddress] { - guard web3.provider.attachedKeystoreManager == nil else { - return try web3.wallet.getAccounts() - } - return try await APIRequest.sendRequest(with: web3.provider, for: .getAccounts).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBalance.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBalance.swift deleted file mode 100755 index fb189e2fa..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBalance.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import Core -import BigInt - -extension Web3.Eth { - public func getBalance(for address: EthereumAddress, onBlock: BlockNumber = .latest) async throws -> BigUInt { - let request = APIRequest.getBalance(address.address, onBlock) - return try await APIRequest.sendRequest(with: web3.provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByHash.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByHash.swift deleted file mode 100755 index ad68b740c..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByHash.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func block(by hash: Data, fullTransactions: Bool = false) async throws -> Block { - let request: APIRequest = .getBlockByHash(hash.toHexString().addHexPrefix(), fullTransactions) - return try await APIRequest.sendRequest(with: provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByNumber.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByNumber.swift deleted file mode 100755 index 6763df2cc..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByNumber.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func block(by hash: Hash, fullTransactions: Bool = false) async throws -> Block { - let request = APIRequest.getBlockByHash(hash, fullTransactions) - return try await APIRequest.sendRequest(with: provider, for: request).result - } - - public func block(by number: BlockNumber, fullTransactions: Bool = false) async throws -> Block { - let request = APIRequest.getBlockByNumber(number, fullTransactions) - return try await APIRequest.sendRequest(with: provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockNumber.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockNumber.swift deleted file mode 100755 index b63556897..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockNumber.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func blockNumber() async throws -> BigUInt { - try await APIRequest.sendRequest(with: web3.provider, for: .blockNumber).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetCode.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetCode.swift deleted file mode 100644 index f17eda612..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetCode.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import Core -import BigInt - -extension Web3.Eth { - public func code(for address: EthereumAddress, onBlock: BlockNumber = .latest) async throws -> Hash { - let request = APIRequest.getCode(address.address, onBlock) - return try await APIRequest.sendRequest(with: provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetGasPrice.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetGasPrice.swift deleted file mode 100755 index e902e7736..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetGasPrice.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func gasPrice() async throws -> BigUInt { - try await APIRequest.sendRequest(with: self.provider, for: .gasPrice).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetTransactionCount.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetTransactionCount.swift deleted file mode 100755 index 59ff77d35..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetTransactionCount.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func getTransactionCount(for address: EthereumAddress, onBlock: BlockNumber = .latest) async throws -> BigUInt { - let request = APIRequest.getTransactionCount(address.address, onBlock) - return try await APIRequest.sendRequest(with: provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetTransactionDetails.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetTransactionDetails.swift deleted file mode 100755 index 34a30a98c..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetTransactionDetails.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func transactionDetails(_ txHash: Data) async throws -> TransactionDetails { - let request = APIRequest.getTransactionByHash(txHash.toHexString().addHexPrefix()) - return try await APIRequest.sendRequest(with: provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetTransactionReceipt.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetTransactionReceipt.swift deleted file mode 100755 index a960c5791..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetTransactionReceipt.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func transactionReceipt(_ txHash: Data) async throws -> TransactionReceipt { - let request = APIRequest.getTransactionReceipt(txHash.toHexString().addHexPrefix()) - return try await APIRequest.sendRequest(with: provider, for: request).result - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendRawTransaction.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendRawTransaction.swift deleted file mode 100755 index a74bed92f..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendRawTransaction.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Yaroslav Yashin. -// Copyright © 2022 Yaroslav Yashin. All rights reserved. -// - -import Foundation -import Core - -extension Web3.Eth { - public func send(raw data: Data) async throws -> TransactionSendingResult { - let request = APIRequest.sendRawTransaction(data.toHexString().addHexPrefix()) - let response: APIResponse = try await APIRequest.sendRequest(with: provider, for: request) - return try TransactionSendingResult(data: data, hash: response.result) - } -} - -public struct TransactionSendingResult { - public var transaction: CodableTransaction - public var hash: String -} - -fileprivate extension TransactionSendingResult { - init(data: Data, hash: Hash) throws { - guard let transaction = CodableTransaction(rawValue: data) else { throw Web3Error.dataError } - self.transaction = transaction - self.hash = hash - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendTransaction.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendTransaction.swift deleted file mode 100644 index 51f9fe86a..000000000 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendTransaction.swift +++ /dev/null @@ -1,17 +0,0 @@ -// web3swift -// -// Created by Alex Vlasov. -// Copyright © 2018 Alex Vlasov. All rights reserved. -// - -import Foundation -import BigInt -import Core - -extension Web3.Eth { - public func send(_ transaction: CodableTransaction) async throws -> TransactionSendingResult { - let request = APIRequest.sendTransaction(transaction) - let response: APIResponse = try await APIRequest.sendRequest(with: provider, for: request) - return TransactionSendingResult(transaction: transaction, hash: response.result) - } -} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/IEth+Defaults.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/IEth+Defaults.swift new file mode 100644 index 000000000..3dcade8c0 --- /dev/null +++ b/Sources/web3swift/EthereumAPICalls/Ethereum/IEth+Defaults.swift @@ -0,0 +1,158 @@ +// +// IEth+Defaults.swift +// +// Created by JeneaVranceanu on 07.12.2022. +// + +import Foundation +import BigInt +import Core + +public extension IEth { + func callTransaction(_ transaction: CodableTransaction) async throws -> Data { + let request = APIRequest.call(transaction, transaction.callOnBlock ?? .latest) + return try await APIRequest.sendRequest(with: provider, for: request).result + } +} + +public extension IEth { + func estimateGas(for transaction: CodableTransaction) async throws -> BigUInt { + try await estimateGas(for: transaction, onBlock: .latest) + } + + func estimateGas(for transaction: CodableTransaction, onBlock: BlockNumber) async throws -> BigUInt { + let request = APIRequest.estimateGas(transaction, onBlock) + return try await APIRequest.sendRequest(with: provider, for: request).result + } +} + +public extension IEth { + func transactionReceipt(_ txHash: Data) async throws -> TransactionReceipt { + let request = APIRequest.getTransactionReceipt(txHash.toHexString().addHexPrefix()) + return try await APIRequest.sendRequest(with: provider, for: request).result + } +} + +public extension IEth { + func transactionDetails(_ txHash: Data) async throws -> TransactionDetails { + let request = APIRequest.getTransactionByHash(txHash.toHexString().addHexPrefix()) + return try await APIRequest.sendRequest(with: provider, for: request).result + } +} + +public extension IEth { + func getTransactionCount(for address: EthereumAddress) async throws -> BigUInt { + try await getTransactionCount(for: address, onBlock: .latest) + } + + func getTransactionCount(for address: EthereumAddress, onBlock: BlockNumber) async throws -> BigUInt { + let request = APIRequest.getTransactionCount(address.address, onBlock) + return try await APIRequest.sendRequest(with: provider, for: request).result + } +} + +public extension IEth { + func gasPrice() async throws -> BigUInt { + try await APIRequest.sendRequest(with: self.provider, for: .gasPrice).result + } +} + +public extension IEth { + func code(for address: EthereumAddress) async throws -> Hash { + try await code(for: address, onBlock: .latest) + } + + func code(for address: EthereumAddress, onBlock: BlockNumber) async throws -> Hash { + let request = APIRequest.getCode(address.address, onBlock) + return try await APIRequest.sendRequest(with: provider, for: request).result + } +} + +public extension IEth { + func blockNumber() async throws -> BigUInt { + try await APIRequest.sendRequest(with: provider, for: .blockNumber).result + } +} + +public extension IEth { + func block(by hash: Hash) async throws -> Block { + try await block(by: hash, fullTransactions: false) + } + + func block(by hash: Hash, fullTransactions: Bool) async throws -> Block { + let request = APIRequest.getBlockByHash(hash, fullTransactions) + return try await APIRequest.sendRequest(with: provider, for: request).result + } + + func block(by number: BlockNumber) async throws -> Block { + try await block(by: number, fullTransactions: false) + } + + func block(by number: BlockNumber, fullTransactions: Bool) async throws -> Block { + let request = APIRequest.getBlockByNumber(number, fullTransactions) + return try await APIRequest.sendRequest(with: provider, for: request).result + } + + func block(by hash: Data) async throws -> Block { + try await block(by: hash, fullTransactions: false) + } + + func block(by hash: Data, fullTransactions: Bool) async throws -> Block { + let request: APIRequest = .getBlockByHash(hash.toHexString().addHexPrefix(), fullTransactions) + return try await APIRequest.sendRequest(with: provider, for: request).result + } +} + +public extension IEth { + func getBalance(for address: EthereumAddress) async throws -> BigUInt { + try await getBalance(for: address, onBlock: .latest) + } + + func getBalance(for address: EthereumAddress, onBlock: BlockNumber) async throws -> BigUInt { + let request = APIRequest.getBalance(address.address, onBlock) + return try await APIRequest.sendRequest(with: provider, for: request).result + } +} + +public extension IEth { + func ownedAccounts() async throws -> [EthereumAddress] { + if let addresses = provider.attachedKeystoreManager?.addresses { + return addresses + } + return try await APIRequest.sendRequest(with: provider, for: .getAccounts).result + } +} + +public extension IEth { + func feeHistory(blockCount: BigUInt, block: BlockNumber, percentiles: [Double]) async throws -> Oracle.FeeHistory { + let request = APIRequest.feeHistory(blockCount, block, percentiles) + return try await APIRequest.sendRequest(with: provider, for: request).result + } +} + +public extension IEth { + func send(_ transaction: CodableTransaction) async throws -> TransactionSendingResult { + let request = APIRequest.sendTransaction(transaction) + let response: APIResponse = try await APIRequest.sendRequest(with: provider, for: request) + return TransactionSendingResult(transaction: transaction, hash: response.result) + } + + func send(raw data: Data) async throws -> TransactionSendingResult { + guard let transaction = CodableTransaction(rawValue: data) else { + // FIXME: When the PR is merged add this description to dataError -> + // Description to add: + // Link to PR: + throw Web3Error.dataError + } + let request = APIRequest.sendRawTransaction(data.toHexString().addHexPrefix()) + let response: APIResponse = try await APIRequest.sendRequest(with: provider, for: request) + return TransactionSendingResult(transaction: transaction, hash: response.result) + } +} + +// MARK: - Supporting models and extensions + +public struct TransactionSendingResult { + public var transaction: CodableTransaction + public var hash: String +} diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/IEth.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/IEth.swift new file mode 100755 index 000000000..69bbc3e5a --- /dev/null +++ b/Sources/web3swift/EthereumAPICalls/Ethereum/IEth.swift @@ -0,0 +1,34 @@ +// +// Created by Yaroslav Yashin. +// Copyright © 2022 Yaroslav Yashin. All rights reserved. +// + +import Foundation +import BigInt +import Core + +public protocol IEth { + var provider: Web3Provider { get } + func callTransaction(_ transaction: CodableTransaction) async throws -> Data + func send(_ transaction: CodableTransaction) async throws -> TransactionSendingResult + func send(raw data: Data) async throws -> TransactionSendingResult + + func estimateGas(for transaction: CodableTransaction, onBlock: BlockNumber) async throws -> BigUInt + func feeHistory(blockCount: BigUInt, block: BlockNumber, percentiles: [Double]) async throws -> Oracle.FeeHistory + func ownedAccounts() async throws -> [EthereumAddress] + func getBalance(for address: EthereumAddress, onBlock: BlockNumber) async throws -> BigUInt + + func block(by hash: Data, fullTransactions: Bool) async throws -> Block + func block(by number: BlockNumber, fullTransactions: Bool) async throws -> Block + func block(by hash: Hash, fullTransactions: Bool) async throws -> Block + func blockNumber() async throws -> BigUInt + + func code(for address: EthereumAddress, onBlock: BlockNumber) async throws -> Hash + + func gasPrice() async throws -> BigUInt + + func getTransactionCount(for address: EthereumAddress, onBlock: BlockNumber) async throws -> BigUInt + + func transactionDetails(_ txHash: Data) async throws -> TransactionDetails + func transactionReceipt(_ txHash: Data) async throws -> TransactionReceipt +} diff --git a/Sources/web3swift/Web3/Web3+Instance.swift b/Sources/web3swift/Web3/Web3+Instance.swift index 1e54f5835..a9b439a56 100755 --- a/Sources/web3swift/Web3/Web3+Instance.swift +++ b/Sources/web3swift/Web3/Web3+Instance.swift @@ -20,29 +20,26 @@ public class Web3 { /// Keystore manager can be bound to Web3 instance. If some manager is bound all further account related functions, such /// as account listing, transaction signing, etc. are done locally using private keys and accounts found in a manager. public func addKeystoreManager(_ manager: KeystoreManager?) { - self.provider.attachedKeystoreManager = manager + provider.attachedKeystoreManager = manager } - var ethInstance: Web3.Eth? + var ethInstance: IEth? /// Public web3.eth.* namespace. - public var eth: Web3.Eth { - if self.ethInstance != nil { - return self.ethInstance! + public var eth: IEth { + if ethInstance != nil { + return ethInstance! } - self.ethInstance = Web3.Eth(provider: self.provider, web3: self) - return self.ethInstance! + ethInstance = Web3.Eth(provider: provider) + return ethInstance! } // FIXME: Rewrite this to CodableTransaction - public class Eth { - var provider: Web3Provider - // weak var web3: web3? - var web3: Web3 + public class Eth: IEth { + public var provider: Web3Provider - public init(provider prov: Web3Provider, web3 web3instance: Web3) { + public init(provider prov: Web3Provider) { provider = prov - web3 = web3instance } } @@ -60,7 +57,7 @@ public class Web3 { // FIXME: Rewrite this to CodableTransaction public class Personal { var provider: Web3Provider - // weak var web3: web3? + // FIXME: remove dependency on web3 instance!! var web3: Web3 public init(provider prov: Web3Provider, web3 web3instance: Web3) { provider = prov @@ -82,7 +79,7 @@ public class Web3 { // FIXME: Rewrite this to CodableTransaction public class TxPool { var provider: Web3Provider - // weak var web3: web3? + // FIXME: remove dependency on web3 instance!! var web3: Web3 public init(provider prov: Web3Provider, web3 web3instance: Web3) { provider = prov @@ -103,7 +100,7 @@ public class Web3 { public class Web3Wallet { var provider: Web3Provider - // weak var web3: web3? + // FIXME: remove dependency on web3 instance!! var web3: Web3 public init(provider prov: Web3Provider, web3 web3instance: Web3) { provider = prov @@ -125,7 +122,7 @@ public class Web3 { // FIXME: Rewrite this to CodableTransaction public class BrowserFunctions { var provider: Web3Provider - // weak var web3: web3? + // FIXME: remove dependency on web3 instance!! public var web3: Web3 public init(provider prov: Web3Provider, web3 web3instance: Web3) { provider = prov @@ -156,7 +153,7 @@ public class Web3 { } var provider: Web3Provider - // weak var web3: web3? + // FIXME: remove dependency on web3 instance!! var web3: Web3 var timer: RepeatingTimer? diff --git a/Tests/web3swiftTests/localTests/ABIEncoderTest.swift b/Tests/web3swiftTests/localTests/ABIEncoderTest.swift index 9e36c9b4d..2e39c6e58 100644 --- a/Tests/web3swiftTests/localTests/ABIEncoderTest.swift +++ b/Tests/web3swiftTests/localTests/ABIEncoderTest.swift @@ -13,7 +13,7 @@ import Core class ABIEncoderTest: XCTestCase { - func test_soliditySha3() throws { + func testSoliditySha3() throws { var hex = try ABIEncoder.soliditySha3(true).toHexString().addHexPrefix() assert(hex == "0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2") hex = try ABIEncoder.soliditySha3(-10).toHexString().addHexPrefix() @@ -66,7 +66,7 @@ class ABIEncoderTest: XCTestCase { assert(hex == "0xa13b31627c1ed7aaded5aecec71baf02fe123797fffd45e662eac8e06fbe4955") } - func test_soliditySha3Fail_FloatDouble() throws { + func testSoliditySha3FailFloatDouble() throws { assert((try? ABIEncoder.soliditySha3(Float(1))) == nil) assert((try? ABIEncoder.soliditySha3(Double(1))) == nil) assert((try? ABIEncoder.soliditySha3(CGFloat(1))) == nil) @@ -78,7 +78,7 @@ class ABIEncoderTest: XCTestCase { /// `[AnyObject]` is not allowed to be used directly as input for `solidtySha3`. /// `AnyObject` erases type data making it impossible to encode some types correctly, /// e.g.: Bool can be treated as Int (8/16/32/64) and 0/1 numbers can be treated as Bool. - func test_soliditySha3Fail_1() throws { + func testSoliditySha3Fail_1() throws { var didFail = false do { _ = try ABIEncoder.soliditySha3([""] as [AnyObject]) @@ -91,7 +91,7 @@ class ABIEncoderTest: XCTestCase { /// `AnyObject` is not allowed to be used directly as input for `solidtySha3`. /// `AnyObject` erases type data making it impossible to encode some types correctly, /// e.g.: Bool can be treated as Int (8/16/32/64) and 0/1 numbers can be treated as Bool. - func test_soliditySha3Fail_2() throws { + func testSoliditySha3Fail_2() throws { var didFail = false do { _ = try ABIEncoder.soliditySha3("" as AnyObject) @@ -101,7 +101,7 @@ class ABIEncoderTest: XCTestCase { XCTAssertTrue(didFail) } - func test_abiEncoding_emptyValues() { + func testAbiEncodingEmptyValues() { let zeroBytes = ABIEncoder.encode(types: [ABI.Element.InOut](), values: [AnyObject]())! XCTAssert(zeroBytes.count == 0) @@ -114,4 +114,24 @@ class ABIEncoderTest: XCTestCase { XCTAssertTrue(functionWithNoInput.methodEncoding == encodedFunction) XCTAssertTrue("0xe16b4a9b" == encodedFunction?.toHexString().addHexPrefix().lowercased()) } + + func testAbiEncodingDynamicTypes() { + var encodedValue = ABIEncoder.encode(types: [.dynamicBytes], values: [Data.fromHex("6761766f66796f726b")!] as [AnyObject])!.toHexString() + XCTAssertEqual(encodedValue, "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000096761766f66796f726b0000000000000000000000000000000000000000000000") + + encodedValue = ABIEncoder.encode(types: [.dynamicBytes], values: [Data.fromHex("731a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b")!] as [AnyObject])!.toHexString() + XCTAssertEqual(encodedValue, "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020731a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b") + + encodedValue = ABIEncoder.encode(types: [.dynamicBytes], values: [Data.fromHex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1")!] as [AnyObject])!.toHexString() + XCTAssertEqual(encodedValue, "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff100") + + encodedValue = ABIEncoder.encode(types: [.dynamicBytes], values: [Data.fromHex("c3a40000c3a4")!] as [AnyObject])!.toHexString() + XCTAssertEqual(encodedValue, "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006c3a40000c3a40000000000000000000000000000000000000000000000000000") + + encodedValue = ABIEncoder.encode(types: [.string], values: ["gavofyork"] as [AnyObject])!.toHexString() + XCTAssertEqual(encodedValue, "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000096761766f66796f726b0000000000000000000000000000000000000000000000") + + encodedValue = ABIEncoder.encode(types: [.string], values: ["Heeäööä👅D34ɝɣ24Єͽ-.,äü+#/"] as [AnyObject])!.toHexString() + XCTAssertEqual(encodedValue, "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000026486565c3a4c3b6c3b6c3a4f09f9185443334c99dc9a33234d084cdbd2d2e2cc3a4c3bc2b232f0000000000000000000000000000000000000000000000000000") + } } diff --git a/Tests/web3swiftTests/localTests/KeystoresTests.swift b/Tests/web3swiftTests/localTests/KeystoresTests.swift index 4911c6c39..3bce14c33 100755 --- a/Tests/web3swiftTests/localTests/KeystoresTests.swift +++ b/Tests/web3swiftTests/localTests/KeystoresTests.swift @@ -16,12 +16,12 @@ class KeystoresTests: LocalTestCase { func testBIP39 () throws { var entropy = Data.fromHex("00000000000000000000000000000000")! var phrase = BIP39.generateMnemonicsFromEntropy(entropy: entropy) - XCTAssert( phrase == "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") + XCTAssert(phrase == "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") var seed = BIP39.seedFromMmemonics(phrase!, password: "TREZOR") XCTAssert(seed?.toHexString() == "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04") entropy = Data.fromHex("68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c")! phrase = BIP39.generateMnemonicsFromEntropy(entropy: entropy) - XCTAssert( phrase == "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length") + XCTAssert(phrase == "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length") seed = BIP39.seedFromMmemonics(phrase!, password: "TREZOR") XCTAssert(seed?.toHexString() == "64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440") } diff --git a/Tests/web3swiftTests/localTests/Mocks.swift b/Tests/web3swiftTests/localTests/Mocks.swift new file mode 100644 index 000000000..de284dd2b --- /dev/null +++ b/Tests/web3swiftTests/localTests/Mocks.swift @@ -0,0 +1,23 @@ +// +// Mocks.swift +// +// Created by JeneaVranceanu on 07.12.2022. +// + +import Foundation +@testable import web3swift +@testable import Core + +class Web3EthMock: IEth { + let provider: Web3Provider + + var onCallTransaction: ((CodableTransaction) -> Data)? + + init(provider: Web3Provider) { + self.provider = provider + } + + func callTransaction(_ transaction: CodableTransaction) async throws -> Data { + onCallTransaction?(transaction) ?? Data() + } +} diff --git a/Tests/web3swiftTests/localTests/ST20AndSecurityTokenTests.swift b/Tests/web3swiftTests/localTests/ST20AndSecurityTokenTests.swift new file mode 100644 index 000000000..738648e7e --- /dev/null +++ b/Tests/web3swiftTests/localTests/ST20AndSecurityTokenTests.swift @@ -0,0 +1,133 @@ +// +// ST20AndSecurityTokenTests.swift +// +// Created by JeneaVranceanu on 07.12.2022. +// + +import XCTest +import BigInt +import Core + +@testable import web3swift + +class ST20AndSecurityTokenTests: XCTestCase { + + var web3: Web3! + var ethMock: Web3EthMock! + var st20token: ST20! + var securityToken: SecurityToken! + + override func setUp() async throws { + web3 = await Web3.InfuraGoerliWeb3(accessToken: Constants.infuraToken) + ethMock = Web3EthMock(provider: web3.provider) + web3.ethInstance = ethMock + st20token = ST20.init(web3: web3, provider: web3.provider, address: .contractDeploymentAddress()) + securityToken = SecurityToken.init(web3: web3, provider: web3.provider, address: .contractDeploymentAddress()) + } + + func testST20TokenPropertiesBasedOnERC20() async throws { + let expectedSymbol = "RandomTokenSymbol953" + let expectedName = "WhatA NAME - l953. Never seen again!" + let expectedDecimals = UInt8.random(in: 0...255) + + ethMock.onCallTransaction = { transaction in + guard let function = self.st20token.contract.contract.getFunctionCalled(transaction.data) else { + XCTFail("Failed to decode function call to determine what shall be returned") + return Data() + } + switch function.name { + case "symbol": + return ABIEncoder.encode(types: [.string], values: [expectedSymbol] as [AnyObject])! + case "name": + return ABIEncoder.encode(types: [.string], values: [expectedName] as [AnyObject])! + case "decimals": + return ABIEncoder.encode(types: [.uint(bits: 8)], values: [expectedDecimals] as [AnyObject])! + default: + // Unexpected function called + XCTFail("Called function '\(String(describing: function.name))' which wasn't supposed to be called.") + return Data() + } + } + + try await st20token.readProperties() + XCTAssertEqual(st20token.symbol, expectedSymbol) + XCTAssertEqual(st20token.name, expectedName) + XCTAssertEqual(st20token.decimals, expectedDecimals) + } + + func testST20TokenBalanceAndAllowance() async throws { + let expectedAllowance = BigUInt.randomInteger(lessThan: BigUInt(10000000000)) + let expectedBalance = BigUInt.randomInteger(lessThan: BigUInt(10000000000)) + + let userAddress = EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")! + let delegate = EthereumAddress("0x2dD33957C90880bE4Ee9fd5F703110BDA2E579EC")! + + ethMock.onCallTransaction = { transaction in + guard let function = self.st20token.contract.contract.getFunctionCalled(transaction.data) else { + XCTFail("Failed to decode function call to determine what shall be returned") + return Data() + } + switch function.name { + case "balanceOf": + let address = function.decodeInputData(transaction.data)?["0"] as? EthereumAddress + XCTAssertEqual(address, userAddress) + return ABIEncoder.encode(types: [.uint(bits: 256)], values: [expectedBalance] as [AnyObject])! + case "allowance": + let transactionInput = function.decodeInputData(transaction.data) + XCTAssertEqual(transactionInput?["0"] as? EthereumAddress, userAddress) + XCTAssertEqual(transactionInput?["1"] as? EthereumAddress, delegate) + return ABIEncoder.encode(types: [.uint(bits: 256)], values: [expectedAllowance] as [AnyObject])! + default: + // Unexpected function called + XCTFail("Called function '\(String(describing: function.name))' which wasn't supposed to be called.") + return Data() + } + } + + let balance = try await st20token.getBalance(account: userAddress) + let allowance = try await st20token.getAllowance(originalOwner: userAddress, delegate: delegate) + XCTAssertEqual(balance, expectedBalance) + XCTAssertEqual(allowance, expectedAllowance) + } + + func testSecurityTokenInvestors() async throws { + let expectedNumberOfInvestors = BigUInt.randomInteger(lessThan: BigUInt(10000000000)) + ethMock.onCallTransaction = { transaction in + guard let function = self.securityToken.contract.contract.getFunctionCalled(transaction.data) else { + XCTFail("Failed to decode function call to determine what shall be returned") + return Data() + } + if function.name == "investorCount" { + return ABIEncoder.encode(types: [.uint(bits: 256)], values: [expectedNumberOfInvestors] as [AnyObject])! + } + // Unexpected function called + XCTFail("Called function '\(String(describing: function.name))' which wasn't supposed to be called.") + return Data() + } + + let investorsCount = try await securityToken.investorCount() + XCTAssertEqual(investorsCount, expectedNumberOfInvestors) + } + + func testSecurityTokenGranularity() async throws { + let expectedGranularity = BigUInt.randomInteger(lessThan: BigUInt(10000000000)) + + ethMock.onCallTransaction = { transaction in + guard let function = self.securityToken.contract.contract.getFunctionCalled(transaction.data) else { + XCTFail("Failed to decode function call to determine what shall be returned") + return Data() + } + switch function.name { + case "granularity": + return ABIEncoder.encode(types: [.uint(bits: 256)], values: [expectedGranularity] as [AnyObject])! + default: + // Unexpected function called + XCTFail("Called function '\(String(describing: function.name))' which wasn't supposed to be called.") + return Data() + } + } + + let granularity = try await securityToken.getGranularity() + XCTAssertEqual(granularity, expectedGranularity) + } +} diff --git a/Tests/web3swiftTests/remoteTests/ST20AndSecurityTokenTests.swift b/Tests/web3swiftTests/remoteTests/ST20AndSecurityTokenTests.swift deleted file mode 100644 index be8d5b234..000000000 --- a/Tests/web3swiftTests/remoteTests/ST20AndSecurityTokenTests.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// web3swift_ST20_Tests.swift -// web3swift-iOS_Tests -// -// Created by Anton on 15/03/2019. -// Copyright © 2019 The Matter Inc. All rights reserved. -// -import XCTest -import BigInt -import web3swift -import Core - -@testable import web3swift - -// MARK: Works only with network connection -class ST20AndSecurityTokenTests: XCTestCase { - - // FIXME: Enable me back again - // Test fails because there's no such wallet on goerli chain as well as token. -// func testERC20TokenCreation() async throws { -// let web3 = await Web3.InfuraGoerliWeb3(accessToken: Constants.infuraToken) -// let w3sTokenAddress = EthereumAddress("0x33d191db2486e0d245b44fde3fae5ed667d5694b")! -// let st20token = ST20.init(web3: web3, provider: web3.provider, address: w3sTokenAddress) -// try await st20token.readProperties() -// XCTAssertEqual(st20token.symbol(), "MIMI") -// XCTAssertEqual(st20token.name(), "Mimi") -// XCTAssertEqual(st20token.decimals(), 18) -// } - - func testST20tokenBalanceAndAllowance() async throws { - let web3 = await Web3.InfuraGoerliWeb3(accessToken: Constants.infuraToken) - let w3sTokenAddress = EthereumAddress("0x2dD33957C90880bE4Ee9fd5F703110BDA2E579EC")! - let st20token = ST20.init(web3: web3, provider: web3.provider, address: w3sTokenAddress) - let userAddress = EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")! - let balance = try await st20token.getBalance(account: userAddress) - let allowance = try await st20token.getAllowance(originalOwner: userAddress, delegate: userAddress) - XCTAssertEqual(balance, 0) - XCTAssertEqual(allowance, 0) - } - - func testSecurityTokenInvestors() async throws { - let web3 = await Web3.InfuraGoerliWeb3(accessToken: Constants.infuraToken) - let w3sTokenAddress = EthereumAddress("0x2dD33957C90880bE4Ee9fd5F703110BDA2E579EC")! - let stoken = SecurityToken.init(web3: web3, provider: web3.provider, address: w3sTokenAddress) - let investorsCount = try await stoken.investorCount() - XCTAssertEqual(investorsCount, 0) - } - - // FIXME: Enable me back again - // Test fails because there's no such wallet on goerli chain as well as token. -// func testSecurityTokenGranularity() async throws { -// let web3 = await Web3.InfuraGoerliWeb3(accessToken: Constants.infuraToken) -// let w3sTokenAddress = EthereumAddress("0x2dD33957C90880bE4Ee9fd5F703110BDA2E579EC")! -// let stoken = SecurityToken.init(web3: web3, provider: web3.provider, address: w3sTokenAddress) -// let granularity = try await stoken.getGranularity() -// let stringGranularity = String(granularity) -// XCTAssertEqual(stringGranularity, "1000000000000000000") -// } -}