diff --git a/.gitignore b/.gitignore index 7f22cabd..af2222c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .build/ .vscode/launch.json +.env diff --git a/.swiftlint.yml b/.swiftlint.yml index 93d6cbb0..8e141927 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -17,6 +17,19 @@ disabled_rules: - line_length # not a very useful lint honestly. - cyclomatic_complexity + # it's basically exclusively used for `unreachable` + - force_try + # This triggers a lot because of functions that simply cannot be smaller. + - function_body_length + # see above. + - closure_body_length + # And this triggers a lot because of the above two. + - type_body_length + # likewise here + - file_length + # Identifier names should make sense, + # and renaming stuff isn't always the right decision. + - identifier_name opt_in_rules: # deprecated & will be removed @@ -51,6 +64,7 @@ opt_in_rules: # - extension_access_modifier - fallthrough - fatal_error_message + - missing_docs analyzer_rules: - capture_variable diff --git a/Examples/CreateAccount/main.swift b/Examples/CreateAccount/main.swift index d756fe26..4f4488d9 100644 --- a/Examples/CreateAccount/main.swift +++ b/Examples/CreateAccount/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -47,15 +47,20 @@ public enum Program { } extension Environment { - public var operatorAccountId: AccountId { + /// Account ID for the operator to use in this example. + internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } - public var operatorKey: PrivateKey { + /// Private key for the operator to use in this example. + internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/CreateFile/main.swift b/Examples/CreateFile/main.swift index 60444b9c..c61a5fbf 100644 --- a/Examples/CreateFile/main.swift +++ b/Examples/CreateFile/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -48,15 +48,20 @@ public enum Program { } extension Environment { - public var operatorAccountId: AccountId { + /// Account ID for the operator to use in this example. + internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } - public var operatorKey: PrivateKey { + /// Private key for the operator to use in this example. + internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/CreateTopic/main.swift b/Examples/CreateTopic/main.swift index 023b90a5..b016db24 100644 --- a/Examples/CreateTopic/main.swift +++ b/Examples/CreateTopic/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -46,16 +46,22 @@ public enum Program { print("sequence number = \(sendReceipt.topicSequenceNumber)") } } + extension Environment { - public var operatorAccountId: AccountId { + /// Account ID for the operator to use in this example. + internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } - public var operatorKey: PrivateKey { + /// Private key for the operator to use in this example. + internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/DeleteAccount/main.swift b/Examples/DeleteAccount/main.swift index ea3ec7c6..bd589958 100644 --- a/Examples/DeleteAccount/main.swift +++ b/Examples/DeleteAccount/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -39,15 +39,20 @@ public enum Program { } extension Environment { + /// Account ID for the operator to use in this example. internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } + /// Private key for the operator to use in this example. internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/DeleteFile/main.swift b/Examples/DeleteFile/main.swift index f2406e64..fc355175 100644 --- a/Examples/DeleteFile/main.swift +++ b/Examples/DeleteFile/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -52,15 +52,20 @@ public enum Program { } extension Environment { - public var operatorAccountId: AccountId { + /// Account ID for the operator to use in this example. + internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } - public var operatorKey: PrivateKey { + /// Private key for the operator to use in this example. + internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/FileAppendChunked/main.swift b/Examples/FileAppendChunked/main.swift index 57de24a3..9f09aa83 100644 --- a/Examples/FileAppendChunked/main.swift +++ b/Examples/FileAppendChunked/main.swift @@ -51,8 +51,8 @@ private let bigContents = """ """ @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -88,15 +88,20 @@ public enum Program { } extension Environment { + /// Account ID for the operator to use in this example. internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } + /// Private key for the operator to use in this example. internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/GenerateKey/main.swift b/Examples/GenerateKey/main.swift index b4eae17c..9b26fb33 100644 --- a/Examples/GenerateKey/main.swift +++ b/Examples/GenerateKey/main.swift @@ -22,8 +22,8 @@ import Foundation import Hedera @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { // Generate a Ed25519 key // This is the current recommended default for Hedera diff --git a/Examples/GenerateKeyWithMnemonic/main.swift b/Examples/GenerateKeyWithMnemonic/main.swift index 5a4766c3..64a622aa 100644 --- a/Examples/GenerateKeyWithMnemonic/main.swift +++ b/Examples/GenerateKeyWithMnemonic/main.swift @@ -22,8 +22,8 @@ import Foundation import Hedera @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { do { let mnemonic = Mnemonic.generate24() let privateKey = try mnemonic.toPrivateKey() diff --git a/Examples/GetAccountBalance/main.swift b/Examples/GetAccountBalance/main.swift index a590036c..92d56cc8 100644 --- a/Examples/GetAccountBalance/main.swift +++ b/Examples/GetAccountBalance/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -38,7 +38,10 @@ public enum Program { } extension Environment { - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/GetAccountInfo/main.swift b/Examples/GetAccountInfo/main.swift index bab1b9ea..8848aa74 100644 --- a/Examples/GetAccountInfo/main.swift +++ b/Examples/GetAccountInfo/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -40,15 +40,20 @@ public enum Program { } extension Environment { + /// Account ID for the operator to use in this example. internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } + /// Private key for the operator to use in this example. internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/GetAddressBook/main.swift b/Examples/GetAddressBook/main.swift index f2e826a2..f6c409cb 100644 --- a/Examples/GetAddressBook/main.swift +++ b/Examples/GetAddressBook/main.swift @@ -23,8 +23,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -39,7 +39,10 @@ public enum Program { } extension Environment { - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/GetExchangeRates/main.swift b/Examples/GetExchangeRates/main.swift index cec87e27..100401cb 100644 --- a/Examples/GetExchangeRates/main.swift +++ b/Examples/GetExchangeRates/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -50,21 +50,27 @@ public enum Program { } extension Environment { - public var operatorAccountId: AccountId { + /// Account ID for the operator to use in this example. + internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } - public var operatorKey: PrivateKey { + /// Private key for the operator to use in this example. + internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } + /// The file ID for the exchange rates file. + /// + /// By default this is `FileId.exchangeRates`. public var exchangeRatesFile: FileId { - // we really do want to abort if the value is invalid - // swiftlint:disable:next force_try try! (self["HEDERA_EXCHANGE_RATES_FILE"]?.stringValue).map(FileId.fromString) ?? FileId.exchangeRates } } diff --git a/Examples/GetFileContents/main.swift b/Examples/GetFileContents/main.swift index de4775e7..63b21daa 100644 --- a/Examples/GetFileContents/main.swift +++ b/Examples/GetFileContents/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -40,15 +40,20 @@ public enum Program { } extension Environment { - public var operatorAccountId: AccountId { + /// Account ID for the operator to use in this example. + internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } - public var operatorKey: PrivateKey { + /// Private key for the operator to use in this example. + internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/Prng/main.swift b/Examples/Prng/main.swift index 43e71183..5062bbc4 100644 --- a/Examples/Prng/main.swift +++ b/Examples/Prng/main.swift @@ -23,8 +23,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -44,15 +44,20 @@ public enum Program { } extension Environment { + /// Account ID for the operator to use in this example. internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } + /// Private key for the operator to use in this example. internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/TopicWithAdminKey/main.swift b/Examples/TopicWithAdminKey/main.swift index 20283196..f583f893 100644 --- a/Examples/TopicWithAdminKey/main.swift +++ b/Examples/TopicWithAdminKey/main.swift @@ -22,8 +22,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -95,15 +95,20 @@ public enum Program { } extension Environment { - public var operatorAccountId: AccountId { + /// Account ID for the operator to use in this example. + internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } - public var operatorKey: PrivateKey { + /// Private key for the operator to use in this example. + internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/TransferCrypto/main.swift b/Examples/TransferCrypto/main.swift index 44b1a6c3..5259d134 100644 --- a/Examples/TransferCrypto/main.swift +++ b/Examples/TransferCrypto/main.swift @@ -23,8 +23,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = Client.forTestnet() @@ -45,15 +45,20 @@ public enum Program { } extension Environment { + /// Account ID for the operator to use in this example. internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } + /// Private key for the operator to use in this example. internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/TransferTokens/main.swift b/Examples/TransferTokens/main.swift index dd465833..237ad621 100644 --- a/Examples/TransferTokens/main.swift +++ b/Examples/TransferTokens/main.swift @@ -23,8 +23,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -176,15 +176,20 @@ public enum Program { } extension Environment { + /// Account ID for the operator to use in this example. internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } + /// Private key for the operator to use in this example. internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/UpdateAccountPublicKey/main.swift b/Examples/UpdateAccountPublicKey/main.swift index a008cfbf..1bfccc3a 100644 --- a/Examples/UpdateAccountPublicKey/main.swift +++ b/Examples/UpdateAccountPublicKey/main.swift @@ -3,8 +3,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -51,15 +51,20 @@ public enum Program { } extension Environment { + /// Account ID for the operator to use in this example. internal var operatorAccountId: AccountId { AccountId(self["OPERATOR_ACCOUNT_ID"]!.stringValue)! } + /// Private key for the operator to use in this example. internal var operatorKey: PrivateKey { PrivateKey(self["OPERATOR_KEY"]!.stringValue)! } - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Examples/ValidateChecksum/main.swift b/Examples/ValidateChecksum/main.swift index 56a45037..f370b365 100644 --- a/Examples/ValidateChecksum/main.swift +++ b/Examples/ValidateChecksum/main.swift @@ -23,8 +23,8 @@ import Hedera import SwiftDotenv @main -public enum Program { - public static func main() async throws { +internal enum Program { + internal static func main() async throws { let env = try Dotenv.load() let client = try Client.forName(env.networkName) @@ -117,7 +117,10 @@ public enum Program { } extension Environment { - public var networkName: String { + /// The name of the hedera network this example should be ran against. + /// + /// Testnet by default. + internal var networkName: String { self["HEDERA_NETWORK"]?.stringValue ?? "testnet" } } diff --git a/Sources/Hedera/Account/AccountBalance.swift b/Sources/Hedera/Account/AccountBalance.swift index 9e5a235b..7302ae7b 100644 --- a/Sources/Hedera/Account/AccountBalance.swift +++ b/Sources/Hedera/Account/AccountBalance.swift @@ -81,14 +81,21 @@ public struct AccountBalance { @available(*, deprecated, message: "use a mirror query") public var tokenDecimals: [TokenId: UInt32] { tokenDecimalsInner } + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { - self.toProtobufBytes() + toProtobufBytes() } + /// Encode self as a string. public func toString() -> String { String(describing: self) } diff --git a/Sources/Hedera/Account/AccountInfo.swift b/Sources/Hedera/Account/AccountInfo.swift index 0f5ca3f1..2f0ccbd7 100644 --- a/Sources/Hedera/Account/AccountInfo.swift +++ b/Sources/Hedera/Account/AccountInfo.swift @@ -154,12 +154,18 @@ public struct AccountInfo { /// Staking metadata for this account. public let staking: StakingInfo? + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { - self.toProtobufBytes() + toProtobufBytes() } } diff --git a/Sources/Hedera/Account/AccountInfoFlow.swift b/Sources/Hedera/Account/AccountInfoFlow.swift index 6fbb24f7..3d240183 100644 --- a/Sources/Hedera/Account/AccountInfoFlow.swift +++ b/Sources/Hedera/Account/AccountInfoFlow.swift @@ -20,6 +20,7 @@ import Foundation +/// Flow for verifying signatures via account info. public enum AccountInfoFlow {} extension AccountInfoFlow { @@ -37,6 +38,7 @@ extension AccountInfoFlow { } } + /// Verify the `signature` for `message` via the given account's public key. public static func verifySignature(_ client: Client, _ accountId: AccountId, _ message: Data, _ signature: Data) async throws { @@ -45,6 +47,7 @@ extension AccountInfoFlow { return try key.verify(message, signature) } + /// Verify the given account's public key has signed the given transaction. public static func verifyTransactionSignature(_ client: Client, _ accountId: AccountId, _ transaction: Transaction) async throws { diff --git a/Sources/Hedera/Contract/ContractCreateFlow.swift b/Sources/Hedera/Contract/ContractCreateFlow.swift index e6974d2d..cef052c7 100644 --- a/Sources/Hedera/Contract/ContractCreateFlow.swift +++ b/Sources/Hedera/Contract/ContractCreateFlow.swift @@ -39,6 +39,7 @@ public final class ContractCreateFlow { case nodeId(UInt64) } + /// Create a new `ContractCreateFlow` with default values. public init() { self.bytecode = Data() self.nodeAccountIds = nil @@ -363,7 +364,7 @@ public final class ContractCreateFlow { /// Sets the client to use for freezing the generated *``ContractCreateTransaction``*. /// - /// By default freezing will use the client provided to ``execute``. + /// By default freezing will use the client provided to ``execute(_:_:)``. /// /// >Note: This *only* affects the ``ContractCreateTransaction`` currently, that is not guaranteed to always be the case. /// diff --git a/Sources/Hedera/Contract/ContractFunctionParameters.swift b/Sources/Hedera/Contract/ContractFunctionParameters.swift index be3c8013..424530cf 100644 --- a/Sources/Hedera/Contract/ContractFunctionParameters.swift +++ b/Sources/Hedera/Contract/ContractFunctionParameters.swift @@ -157,9 +157,11 @@ private struct Argument { } // swiftlint:disable:next type_body_length +/// Builder for encoding parameters for a Solidity contract constructor/function call. public final class ContractFunctionParameters { private var args: [Argument] + /// Create a new contract function parameters builder. public init() { args = [] } @@ -1280,7 +1282,7 @@ public final class ContractFunctionParameters { var staticArgs = Data() var dynamicArgs = Data() - let selector = funcName.map { ContractFunctionSelector($0) } + let selector = funcName.map(ContractFunctionSelector.init) for arg in args { selector?.addParamType(arg.typeName) @@ -1297,7 +1299,7 @@ public final class ContractFunctionParameters { } if let selector = selector { - staticArgs.insert(contentsOf: selector.finish(), at: 0) + staticArgs.insert(contentsOf: selector.finish(), at: staticArgs.startIndex) } return staticArgs + dynamicArgs @@ -1309,7 +1311,7 @@ private func decodeAddress(from description: S) throws -> Evm guard let bytes = Data(hexEncoded: description) else { // todo: better error message - throw HError(kind: .basicParse, description: "invalid evm address") + throw HError.basicParse("invalid evm address") } return try EvmAddress(bytes) diff --git a/Sources/Hedera/Contract/ContractFunctionResult.swift b/Sources/Hedera/Contract/ContractFunctionResult.swift index 2d3b127f..a467aaa3 100644 --- a/Sources/Hedera/Contract/ContractFunctionResult.swift +++ b/Sources/Hedera/Contract/ContractFunctionResult.swift @@ -99,22 +99,31 @@ public struct ContractFunctionResult { getFixedBytesAt(slot: slot, size: UInt(MemoryLayout.size)).flatMap(T.init(bigEndianBytes:)) } + /// Returns the raw bytes that were returned by the contract function. + /// + /// >Tip: While this function does work and is supported, ``bytes is available and is preferred. + /// + /// - Returns: ``bytes``. public func asBytes() -> Data { bytes } + /// Get the value at `index` as a solidity `u8`. public func getUInt8(_ index: UInt) -> UInt8? { getAt(slot: index) } + /// Get the value at `index` as a solidity `i8`. public func getInt8(_ index: UInt) -> Int8? { getAt(slot: index) } + /// Get the value at `index` as a solidity `bool`. public func getBool(_ index: UInt) -> Bool? { getUInt8(index).map { $0 != 0 } } + /// Get the value at `index` as a solidity `u32`. public func getUInt32(_ index: UInt) -> UInt32? { getAt(slot: index) } @@ -134,26 +143,34 @@ public struct ContractFunctionResult { getUInt32At(offset: offset).map(UInt.init) } + /// Get the value at `index` as a solidity `i32`. public func getInt32(_ index: UInt) -> Int32? { self.getAt(slot: index) } + /// Get the value at `index` as a solidity `u64`. public func getUInt64(_ index: UInt) -> UInt64? { self.getAt(slot: index) } + /// Get the value at `index` as a solidity `i64`. public func getInt64(_ index: UInt) -> Int64? { self.getAt(slot: index) } + /// Get the value at `index` as solidity `bytes32`. + /// + /// This is the native word size for the solidity ABI. public func getBytes32(_ index: UInt) -> Data? { self.getFixedBytesAt(slot: index, size: 32).map(Data.init(_:)) } + /// Get the value at `index` as a solidity `address` and then hex-encode the result. public func getAddress(_ index: UInt) -> String? { self.getFixedBytesAt(slot: index, size: 20)?.hexStringEncoded() } + /// Get the value at `index` as solidity `bytes`. public func getBytes(_ index: UInt) -> Data? { guard let offset = getUIntAt(slot: index) else { return nil } guard let len = getUIntAt(offset: offset) else { return nil } @@ -161,10 +178,16 @@ public struct ContractFunctionResult { return bytes.safeSubdata(in: Int(offset + slotSize).. String? { getBytes(index).map { String(decoding: $0, as: UTF8.self) } } + /// Get the value at `index` as a solidity `string[]`. + /// + /// Theoretically, all strings here should be utf8, but this function does _lossy_ conversion. public func getStringArray(_ index: UInt) -> [String]? { guard let offset = getUIntAt(slot: index) else { return nil } guard let count = getUIntAt(offset: offset) else { return nil } @@ -184,10 +207,16 @@ public struct ContractFunctionResult { return array } + /// Get the value at `index` as a solidity `i256` (`int`). + /// + /// This is the native unsigned integer size for the solidity ABI. public func getInt256(_ index: UInt) -> BigInt? { self.getBytes32(index).map { BigInt(signedBEBytes: $0) } } + /// Get the value at `index` as a solidity `u256` (`uint`). + /// + /// This is the native unsigned integer size for the solidity ABI. public func getUInt256(_ index: UInt) -> BigInt? { self.getBytes32(index).map { BigInt(unsignedBEBytes: $0) } } diff --git a/Sources/Hedera/Contract/ContractInfo.swift b/Sources/Hedera/Contract/ContractInfo.swift index ce03bd3a..1c529f5b 100644 --- a/Sources/Hedera/Contract/ContractInfo.swift +++ b/Sources/Hedera/Contract/ContractInfo.swift @@ -21,6 +21,7 @@ import Foundation import HederaProtobufs +/// Information about a smart contract instance. public final class ContractInfo { internal init( contractId: ContractId, @@ -92,15 +93,22 @@ public final class ContractInfo { /// The maximum number of tokens that a contract can be implicitly associated with. public let maxAutomaticTokenAssociations: UInt32 + /// Ledger ID for the network the response was returned from. public let ledgerId: LedgerId /// Staking metadata for this contract. public let stakingInfo: StakingInfo + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/Crypto/CryptoAes.swift b/Sources/Hedera/Crypto/CryptoAes.swift index 9c1a7a5d..e08ab064 100644 --- a/Sources/Hedera/Crypto/CryptoAes.swift +++ b/Sources/Hedera/Crypto/CryptoAes.swift @@ -102,12 +102,9 @@ extension Crypto.Aes { case kCCBufferTooSmall: throw Crypto.AesError.bufferTooSmall(available: output.count, needed: dataOutMoved) - case kCCAlignmentError: - throw Crypto.AesError.alignment - case kCCDecodeError: - throw Crypto.AesError.decode - default: - throw Crypto.AesError.other(status) + case kCCAlignmentError: throw Crypto.AesError.alignment + case kCCDecodeError: throw Crypto.AesError.decode + default: throw Crypto.AesError.other(status) } } } diff --git a/Sources/Hedera/Crypto/Pkcs5Pbkdf2.swift b/Sources/Hedera/Crypto/Pkcs5Pbkdf2.swift index f468b550..280262cf 100644 --- a/Sources/Hedera/Crypto/Pkcs5Pbkdf2.swift +++ b/Sources/Hedera/Crypto/Pkcs5Pbkdf2.swift @@ -175,7 +175,7 @@ extension ASN1ObjectIdentifier { } extension Pkcs5.Pbkdf2Prf: DERImplicitlyTaggable { - static var defaultIdentifier: SwiftASN1.ASN1Identifier { + internal static var defaultIdentifier: SwiftASN1.ASN1Identifier { .sequence } diff --git a/Sources/Hedera/Crypto/Pkcs8.swift b/Sources/Hedera/Crypto/Pkcs8.swift index ca05013a..918616d7 100644 --- a/Sources/Hedera/Crypto/Pkcs8.swift +++ b/Sources/Hedera/Crypto/Pkcs8.swift @@ -126,7 +126,7 @@ extension Pkcs8.Version: DERImplicitlyTaggable { .integer } - init(derEncoded: ASN1Node, withIdentifier identifier: ASN1Identifier) throws { + internal init(derEncoded: ASN1Node, withIdentifier identifier: ASN1Identifier) throws { let raw = try Int(derEncoded: derEncoded, withIdentifier: identifier) guard let value = Self(rawValue: raw) else { @@ -137,7 +137,7 @@ extension Pkcs8.Version: DERImplicitlyTaggable { self = value } - func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) + internal func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws { try coder.serialize(self.rawValue) diff --git a/Sources/Hedera/CustomFee.swift b/Sources/Hedera/CustomFee.swift index 5810648a..54fe766e 100644 --- a/Sources/Hedera/CustomFee.swift +++ b/Sources/Hedera/CustomFee.swift @@ -43,12 +43,16 @@ public protocol CustomFee { } extension CustomFee { + /// Sets the account to recieve the custom fee. + @discardableResult public mutating func feeCollectorAccountId(_ feeCollectorAccountId: AccountId) -> Self { self.feeCollectorAccountId = feeCollectorAccountId return self } + /// Set to `true` if all collectors should be exempt from fees, or to false otherwise. + @discardableResult public mutating func allCollectorsAreExempt(_ allCollectorsAreExempt: Bool) -> Self { self.allCollectorsAreExempt = true @@ -59,16 +63,25 @@ extension CustomFee { /// A transfer fee to assess during a `TransferTransaction` that transfers units of /// the token to which the fee is attached. public enum AnyCustomFee { + /// A fee that costs a fixed number of hbar/tokens. case fixed(FixedFee) + /// A fee that costs a fraction of the transferred amount. case fractional(FractionalFee) + /// A fee that charges a royalty for NFT transfers. case royalty(RoyaltyFee) + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { - self.toProtobufBytes() + toProtobufBytes() } } @@ -279,7 +292,6 @@ public struct FixedFee: CustomFee, ValidateChecksums { /// than the given `maximumAmount`. /// /// The denomination is always in units of the token to which this fractional fee is attached. -/// public struct FractionalFee: CustomFee, ValidateChecksums { public var feeCollectorAccountId: AccountId? diff --git a/Sources/Hedera/EntityId.swift b/Sources/Hedera/EntityId.swift index 5509ab7d..23338492 100644 --- a/Sources/Hedera/EntityId.swift +++ b/Sources/Hedera/EntityId.swift @@ -39,14 +39,27 @@ where /// The checksum for this entity ID with respect to *some* ledger ID. var checksum: Checksum? { get } - /// Create an entity ID from the given entity number. + /// Create an entity ID in the default shard and realm with the given entity number. /// /// - Parameters: /// - num: the number for the new entity ID. init(num: UInt64) + /// Creates an entity ID from the given shard, realm, and entity numbers. + /// + /// - Parameters: + /// - shard: the shard that the realm is contained in. + /// - realm: the realm that the entity number is contained in. + /// - num: the entity ID in the given shard and realm. init(shard: UInt64, realm: UInt64, num: UInt64) + /// Creates an entity ID from the given shard, realm, and entity numbers, and with the given checksum. + /// + /// - Parameters: + /// - shard: the shard that the realm is contained in. + /// - realm: the realm that the entity number is contained in. + /// - num: the entity ID in the given shard and realm. + /// - checksum: a 5 character checksum to help ensure a user-entered entity ID is correct. init(shard: UInt64, realm: UInt64, num: UInt64, checksum: Checksum?) /// Parse an entity ID from a string. @@ -61,6 +74,7 @@ where /// Convert this entity ID to bytes. func toBytes() -> Data + /// Convert this entity ID to a string. func toString() -> String func toStringWithChecksum(_ client: Client) throws -> String @@ -79,54 +93,77 @@ extension EntityId { internal var helper: Helper { Helper(self) } + // swiftlint:disable:next missing_docs public init(integerLiteral value: IntegerLiteralType) { self.init(num: value) } + // inherited docs. + // swiftlint:disable:next missing_docs public init(num: UInt64) { self.init(shard: 0, realm: 0, num: num) } + // inherited docs. + // swiftlint:disable:next missing_docs public init(parsing description: S) throws { self = try PartialEntityId(parsing: description).intoNum() } + // inherited docs. + // swiftlint:disable:next missing_docs public init?(_ description: String) { try? self.init(parsing: description) } + // inherited docs. + // swiftlint:disable:next missing_docs public init(stringLiteral value: StringLiteralType) { // Force try here because this is a logic error. // swiftlint:disable:next force_try try! self.init(parsing: value) } + // inherited docs. + // swiftlint:disable:next missing_docs public static func fromString(_ description: S) throws -> Self { try Self(parsing: description) } + // inherited docs. + // swiftlint:disable:next missing_docs public var description: String { helper.description } + // inherited docs. + // swiftlint:disable:next missing_docs public static func fromSolidityAddress(_ description: S) throws -> Self { try SolidityAddress(parsing: description).toEntityId() } + // inherited docs. + // swiftlint:disable:next missing_docs public func toString() -> String { - self.description + String(describing: self) } internal func makeChecksum(ledger ledgerId: LedgerId) -> Checksum { Checksum.generate(for: self, on: ledgerId) } + // inherited docs. + // swiftlint:disable:next missing_docs public func toStringWithChecksum(_ client: Client) -> String { helper.toStringWithChecksum(client) } + // inherited docs. + // swiftlint:disable:next missing_docs public func validateChecksum(_ client: Client) throws { try helper.validateChecksum(on: client) } + // inherited docs. + // swiftlint:disable:next missing_docs public func toSolidityAddress() throws -> String { try String(describing: SolidityAddress(self)) } diff --git a/Sources/Hedera/Ethereum/EthereumData.swift b/Sources/Hedera/Ethereum/EthereumData.swift index db97bc9a..15150c5e 100644 --- a/Sources/Hedera/Ethereum/EthereumData.swift +++ b/Sources/Hedera/Ethereum/EthereumData.swift @@ -20,8 +20,12 @@ import Foundation +/// Data for an ethereum transaction. public enum EthereumData { + /// Data for a legacy ethereum transaction. case legacy(Legacy) + + /// Data for an Eip 1559 ethereum transaction. case eip1559(Eip1559) internal init(rlpBytes bytes: Data) throws { @@ -65,37 +69,8 @@ public enum EthereumData { // swiftlint:disable identifier_name extension EthereumData { + /// Data for a legacy ethereum transaction. public struct Legacy { - internal init( - nonce: Data, - gasPrice: Data, - gasLimit: Data, - to: Data, - value: Data, - callData: Data, - v: Data, - r: Data, - s: Data - ) { - self.nonce = nonce - self.gasPrice = gasPrice - self.gasLimit = gasLimit - self.to = to - self.value = value - self.callData = callData - self.v = v - self.r = r - self.s = s - } - - internal init(rlpBytes: Data) throws { - do { - try self.init(rlp: AnyRlp(raw: rlpBytes)) - } catch { - throw HError.basicParse(String(describing: error)) - } - } - /// Transaction's nonce. public var nonce: Data @@ -111,70 +86,21 @@ extension EthereumData { /// The transaction value. public var value: Data - /// The V value of the signature. - public var v: Data - /// The raw call data. public var callData: Data + /// The V value of the signature. + public var v: Data + /// The R value of the signature. public var r: Data /// The S value of the signature. public var s: Data - - public static func fromBytes(_ bytes: Data) throws -> Self { - try Self(rlpBytes: bytes) - } - - public func toBytes() -> Data { - var encoder = Rlp.Encoder() - encoder.append(self) - return encoder.output - } } + /// Data for an Eip 1559 ethereum transaction. public struct Eip1559 { - internal init( - chainId: Data, - nonce: Data, - maxPriorityGas: Data, - maxGas: Data, - gasLimit: Data, - to: Data, - value: Data, - callData: Data, - accessList: [Data], - recoveryId: Data, - r: Data, - s: Data - ) { - self.chainId = chainId - self.nonce = nonce - self.maxPriorityGas = maxPriorityGas - self.maxGas = maxGas - self.gasLimit = gasLimit - self.to = to - self.value = value - self.callData = callData - self.accessList = accessList - self.recoveryId = recoveryId - self.r = r - self.s = s - } - - internal init(rlpBytes: Data) throws { - guard rlpBytes.first == 2 else { - throw HError.basicParse("Expected eip1559 transaction data to start with 0x02") - } - - do { - try self.init(rlp: AnyRlp(raw: rlpBytes[1...])) - } catch { - throw HError.basicParse(String(describing: error)) - } - } - /// ID of the chain. public var chainId: Data @@ -212,16 +138,6 @@ extension EthereumData { /// The S value of the signature. public var s: Data - - public static func fromBytes(_ bytes: Data) throws -> Self { - try Self(rlpBytes: bytes) - } - - public func toBytes() -> Data { - var encoder = Rlp.Encoder(buffer: Data([0x02])) - encoder.append(self) - return encoder.output - } } } // swiftlint:enable identifier_name @@ -255,6 +171,28 @@ extension EthereumData.Legacy: RlpDecodable, RlpEncodable { } } +extension EthereumData.Legacy { + internal init(rlpBytes: Data) throws { + do { + try self.init(rlp: AnyRlp(raw: rlpBytes)) + } catch { + throw HError.basicParse(String(describing: error)) + } + } + + /// Deserialize legacy ethereum data from RLP encoded bytes. + public static func fromBytes(_ bytes: Data) throws -> Self { + try Self(rlpBytes: bytes) + } + + /// Encode this legacy ethereum data to RLP encoded bytes. + public func toBytes() -> Data { + var encoder = Rlp.Encoder() + encoder.append(self) + return encoder.output + } +} + extension EthereumData.Eip1559: RlpDecodable, RlpEncodable { internal init(rlp: AnyRlp) throws { let expectedElements = 12 @@ -301,3 +239,29 @@ extension EthereumData.Eip1559: RlpDecodable, RlpEncodable { encoder.endList() } } + +extension EthereumData.Eip1559 { + internal init(rlpBytes: Data) throws { + guard rlpBytes.first == 2 else { + throw HError.basicParse("Expected eip1559 transaction data to start with 0x02") + } + + do { + try self.init(rlp: AnyRlp(raw: rlpBytes[1...])) + } catch { + throw HError.basicParse(String(describing: error)) + } + } + + /// Deserialize eip1559 ethereum data from RLP encoded bytes. + public static func fromBytes(_ bytes: Data) throws -> Self { + try Self(rlpBytes: bytes) + } + + /// Encode this eip1559 ethereum data to RLP encoded bytes. + public func toBytes() -> Data { + var encoder = Rlp.Encoder(buffer: Data([0x02])) + encoder.append(self) + return encoder.output + } +} diff --git a/Sources/Hedera/Ethereum/EthereumFlow.swift b/Sources/Hedera/Ethereum/EthereumFlow.swift index dfea878c..98095589 100644 --- a/Sources/Hedera/Ethereum/EthereumFlow.swift +++ b/Sources/Hedera/Ethereum/EthereumFlow.swift @@ -20,11 +20,14 @@ import Foundation +/// Flow for executing ethereum transactions. public final class EthereumFlow { private static let maxEthereumDataSize: Int = 5120 + /// The raw Ethereum transaction (RLP encoded type 0, 1, and 2). public var ethereumData: EthereumData? + /// Sets the raw Ethereum transaction data (RLP encoded type 0, 1, and 2). @discardableResult public func ethereumData(_ data: Data) throws -> Self { ethereumData = try .init(rlpBytes: data) @@ -32,8 +35,10 @@ public final class EthereumFlow { return self } + /// The maximum amount that the payer of the hedera transaction is willing to pay to complete the transaction. public var maxGasAllowance: Hbar? + /// Sets the maximum amount that the payer of the ethereum transaction is willing to pay to complete the transaction. @discardableResult public func maxGasAllowance(_ maxGasAllowance: Hbar) -> Self { self.maxGasAllowance = maxGasAllowance @@ -41,6 +46,7 @@ public final class EthereumFlow { return self } + /// Generates the required transactions and executes them all. public func execute(_ client: Client, _ timeoutPerTansaction: TimeInterval? = nil) async throws -> TransactionResponse { diff --git a/Sources/Hedera/ExchangeRates.swift b/Sources/Hedera/ExchangeRates.swift index 3ee94cd6..3169cc26 100644 --- a/Sources/Hedera/ExchangeRates.swift +++ b/Sources/Hedera/ExchangeRates.swift @@ -21,13 +21,18 @@ import Foundation import HederaProtobufs -/// The current and next exchange rates between Hbar and USD-cents. +/// The current and next exchange rates between ``Hbar`` and USD-cents. public struct ExchangeRates { /// The current exchange rate between Hbar and USD-cents. public let currentRate: ExchangeRate /// The next exchange rate between Hbar and USD-cents. public let nextRate: ExchangeRate + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } @@ -44,6 +49,7 @@ extension ExchangeRates: FromProtobuf { } } +/// Denotes a conversion between Hbars and cents (USD). public struct ExchangeRate { /// Denotes Hbar equivalent to cents (USD). public let hbars: UInt32 diff --git a/Sources/Hedera/FeeSchedule/FeeComponents.swift b/Sources/Hedera/FeeSchedule/FeeComponents.swift index b4b684ec..67c8962a 100644 --- a/Sources/Hedera/FeeSchedule/FeeComponents.swift +++ b/Sources/Hedera/FeeSchedule/FeeComponents.swift @@ -23,6 +23,8 @@ import HederaProtobufs /// The different components used for fee calculation. public struct FeeComponents { + // missing_docs on memberwise init -> fine. + // swiftlint:disable:next missing_docs public init( min: UInt64, max: UInt64, @@ -84,10 +86,16 @@ public struct FeeComponents { /// The price per byte of bandwidth spent for data retrieved from disk for a response. public var responseDiskByte: UInt64 + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/FeeSchedule/FeeData.swift b/Sources/Hedera/FeeSchedule/FeeData.swift index 4331cc48..55e45023 100644 --- a/Sources/Hedera/FeeSchedule/FeeData.swift +++ b/Sources/Hedera/FeeSchedule/FeeData.swift @@ -24,6 +24,8 @@ import HederaProtobufs /// The total fees charged for a transaction, consisting of 3 parts: /// The node fee, the network fee, and the service fee. public struct FeeData { + // missing_docs on memberwise init -> fine. + // swiftlint:disable:next missing_docs public init(node: FeeComponents, network: FeeComponents, service: FeeComponents, kind: FeeDataType) { self.node = node self.network = network @@ -44,10 +46,16 @@ public struct FeeData { /// correlating to the same hedera functionality. public var kind: FeeDataType + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/FeeSchedule/FeeSchedule.swift b/Sources/Hedera/FeeSchedule/FeeSchedule.swift index cbf2a371..a6e28247 100644 --- a/Sources/Hedera/FeeSchedule/FeeSchedule.swift +++ b/Sources/Hedera/FeeSchedule/FeeSchedule.swift @@ -27,6 +27,8 @@ import HederaProtobufs /// /// [Hedera documentation]: https://docs.hedera.com/guides/docs/hedera-api/basic-types/feeschedule public struct FeeSchedule { + // missing_docs on memberwise init -> fine. + // swiftlint:disable:next missing_docs public init(transactionFeeSchedules: [TransactionFeeSchedule] = [], expirationTime: Timestamp) { self.transactionFeeSchedules = transactionFeeSchedules self.expirationTime = expirationTime @@ -38,10 +40,16 @@ public struct FeeSchedule { /// The time this fee schedule will expire at. public var expirationTime: Timestamp + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/FeeSchedule/FeeSchedules.swift b/Sources/Hedera/FeeSchedule/FeeSchedules.swift index 5754c405..131b4a37 100644 --- a/Sources/Hedera/FeeSchedule/FeeSchedules.swift +++ b/Sources/Hedera/FeeSchedule/FeeSchedules.swift @@ -27,6 +27,8 @@ import HederaProtobufs /// /// [Hedera documentation]: https://docs.hedera.com/guides/docs/hedera-api/basic-types/currentandnextfeeschedule public struct FeeSchedules { + // missing_docs on memberwise init -> fine. + // swiftlint:disable:next missing_docs public init(current: FeeSchedule? = nil, next: FeeSchedule? = nil) { self.current = current self.next = next @@ -38,10 +40,16 @@ public struct FeeSchedules { /// The next fee schedule. public var next: FeeSchedule? + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/FeeSchedule/RequestType.swift b/Sources/Hedera/FeeSchedule/RequestType.swift index ee1f125e..5533b997 100644 --- a/Sources/Hedera/FeeSchedule/RequestType.swift +++ b/Sources/Hedera/FeeSchedule/RequestType.swift @@ -25,6 +25,7 @@ import HederaProtobufs /// The functionality provided by Hedera. public enum RequestType { + // fixme: maybe we want to make this `nil` instead? /// UNSPECIFIED - Need to keep first value as unspecified because first element is ignored and not parsed (0 is ignored by parser) case none diff --git a/Sources/Hedera/FeeSchedule/TransactionFeeSchedule.swift b/Sources/Hedera/FeeSchedule/TransactionFeeSchedule.swift index 5581d8af..6c1c1cfc 100644 --- a/Sources/Hedera/FeeSchedule/TransactionFeeSchedule.swift +++ b/Sources/Hedera/FeeSchedule/TransactionFeeSchedule.swift @@ -27,6 +27,8 @@ import HederaProtobufs /// /// [Hedera documentation]: https://docs.hedera.com/guides/docs/hedera-api/basic-types/transactionfeeschedule public struct TransactionFeeSchedule { + // missing_docs on memberwise init -> fine. + // swiftlint:disable:next missing_docs public init(requestType: RequestType, feeData: FeeData? = nil, fees: [FeeData]) { self.requestType = requestType self.feeData = feeData @@ -44,10 +46,16 @@ public struct TransactionFeeSchedule { /// Supports subtype definition. public var fees: [FeeData] + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/File/FileInfo.swift b/Sources/Hedera/File/FileInfo.swift index c93f9f0f..7fc08b86 100644 --- a/Sources/Hedera/File/FileInfo.swift +++ b/Sources/Hedera/File/FileInfo.swift @@ -65,6 +65,7 @@ public final class FileInfo { /// Memo associated with the file. public let fileMemo: String + /// Ledger ID for the network the response was returned from. public let ledgerId: LedgerId /// The auto renew period for this file. @@ -78,10 +79,16 @@ public final class FileInfo { /// > Warning: This not supported on any hedera network at this time. public let autoRenewAccountId: AccountId? + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/FreezeType.swift b/Sources/Hedera/FreezeType.swift index d5164106..53df20b3 100644 --- a/Sources/Hedera/FreezeType.swift +++ b/Sources/Hedera/FreezeType.swift @@ -20,6 +20,7 @@ import HederaProtobufs +/// A kind of freeze that can be executed by the Hedera network. public enum FreezeType { /// An (invalid) default value for this enum, to ensure the client explicitly sets /// the intended type of freeze transaction. diff --git a/Sources/Hedera/Hbar.swift b/Sources/Hedera/Hbar.swift index e942216b..f6f446ed 100644 --- a/Sources/Hedera/Hbar.swift +++ b/Sources/Hedera/Hbar.swift @@ -21,21 +21,63 @@ import Foundation /// Common units of hbar. +/// /// For the most part they follow SI prefix conventions. +/// +/// ## Hbar +/// Hbar is the native currency used by the Hedera network. +/// +/// The base unit of ``Hbar`` is the ``hbar``, the following units are all expressed with values in terms of `hbar`: +/// +/// | Name | Unit | Symbol | Value | +/// | -------- | ------------ | ------ | ---------- | +/// | Tinybar | ``tinybar`` | tℏ | `1e-8` | +/// | Microbar | ``microbar`` | µℏ | `0.000001` | +/// | Millibar | ``millibar`` | mℏ | `0.001` | +/// | Hbar | ``hbar`` | ℏ | `1` | +/// | Kilobar | ``kilobar`` | kℏ | `1000` | +/// | Megabar | ``megabar`` | Mℏ | `1000000` | +/// | Gigabar | ``gigabar`` | Gℏ | `1e9` | public enum HbarUnit: UInt64, LosslessStringConvertible, ExpressibleByStringLiteral { + /// The Tinybar unit of Hbar. + /// + /// >Note: Used natively by the Hedera network. + /// + /// >Tip: There is a list of conversions in the type definition. case tinybar = 1 + + /// The Microbar unit of Hbar. + /// + /// >Tip: There is a list of conversions in the type definition. case microbar = 100 + + /// The Millibar unit of Hbar. + /// + /// >Tip: There is a list of conversions in the type definition. case millibar = 100_000 + + /// The base unit of Hbar. + /// + /// >Tip: There is a list of conversions in the type definition. case hbar = 100_000_000 + + /// The Killobar unit of Hbar. + /// + /// >Tip: There is a list of conversions in the type definition. case kilobar = 100_000_000_000 + + /// The Megabar unit of Hbar. + /// + /// >Tip: There is a list of conversions in the type definition. case megabar = 100_000_000_000_000 - case gigabar = 100_000_000_000_000_000 - public func getSymbol() -> String { - description - } + /// The Gigabar unit of Hbar. + /// + /// >Tip: There is a list of conversions in the type definition. + case gigabar = 100_000_000_000_000_000 - public var description: String { + /// The symbol associated with this unit of Hbar. + public var symbol: String { switch self { case .tinybar: return "tℏ" @@ -54,8 +96,12 @@ public enum HbarUnit: UInt64, LosslessStringConvertible, ExpressibleByStringLite } } + public var description: String { + symbol + } + public init(stringLiteral value: StringLiteralType) { - self.init(value)! + try! self.init(parsing: value) } fileprivate init(parsing description: S) throws { @@ -83,8 +129,9 @@ public enum HbarUnit: UInt64, LosslessStringConvertible, ExpressibleByStringLite try? self.init(parsing: description) } + /// The value of this unit in ``tinybar``. public func tinybar() -> UInt64 { - self.rawValue + rawValue } } @@ -103,16 +150,14 @@ public struct Hbar: LosslessStringConvertible, ExpressibleByIntegerLiteral, /// Create a new Hbar of the specified, possibly fractional value. public init(_ amount: Decimal, _ unit: HbarUnit = .hbar) throws { guard amount.isFinite else { - throw HError(kind: .basicParse, description: "amount must be a finite decimal number") + throw HError.basicParse("amount must be a finite decimal number") } let tinybars = amount * Decimal(unit.rawValue) guard tinybars.isZero || (tinybars.isNormal && tinybars.exponent >= 0) else { - throw HError( - kind: .basicParse, - description: - "amount and unit combination results in a fractional value for tinybar, ensure tinybar value is a whole number" + throw HError.basicParse( + "amount and unit combination results in a fractional value for tinybar, ensure tinybar value is a whole number" ) } @@ -166,22 +211,33 @@ public struct Hbar: LosslessStringConvertible, ExpressibleByIntegerLiteral, self.tinybars = tinybars } - private let tinybars: Int64 + /// The value of `self` in ``HbarUnit/tinybar``. + public let tinybars: Int64 - public func getValue() -> Decimal { - to(.hbar) + /// The value of `self` in ``HbarUnit/hbar``. + public var value: Decimal { + value(in: .hbar) } - public func negated() -> Self { - Self(tinybars: -tinybars) + /// Convert to a decimal value in the given `unit`. + /// + /// - Parameter `unit`: The unit to convert to. + internal func value(in unit: HbarUnit) -> Decimal { + Decimal(tinybars) / Decimal(unit.rawValue) } - /// Convert this hbar value to a different unit. + /// Convert to a decimal value in the given `unit`. + /// + /// - Parameter `unit`: The unit to convert to. public func to(_ unit: HbarUnit) -> Decimal { - Decimal(tinybars) / Decimal(unit.rawValue) + value(in: unit) } - /// Convert this hbar value to Tinybars. + /// Convert this hbar value to ``HbarUnit/tinybar``. + /// + /// >Tip: While this function does work and is supported, ``tinybars`` is available and is preferred. + /// + /// - Returns: ``tinybars`` public func toTinybars() -> Int64 { tinybars } @@ -200,6 +256,35 @@ public struct Hbar: LosslessStringConvertible, ExpressibleByIntegerLiteral, extension Hbar: Equatable, Comparable { @inlinable public static func < (lhs: Hbar, rhs: Hbar) -> Bool { - lhs.toTinybars() < rhs.toTinybars() + lhs.tinybars < rhs.tinybars + } +} + +extension Hbar: AdditiveArithmetic { + public static func + (lhs: Self, rhs: Self) -> Self { + Self(tinybars: lhs.tinybars + rhs.tinybars) + } + + public static func - (lhs: Self, rhs: Self) -> Self { + Self(tinybars: lhs.tinybars - rhs.tinybars) + } + + /// Replaces this value with its additive inverse. + public mutating func negate() { + self = -self + } + + /// Returns the additive inverse the specified vaue. + /// + /// - Returns: The additive inverse of this value. + public static prefix func - (operand: Self) -> Self { + 0 - operand + } + + /// Returns the additive inverse of `self`. + /// + /// Returns: The additive inverse of this value. + public func negated() -> Self { + -self } } diff --git a/Sources/Hedera/NetworkVersionInfo.swift b/Sources/Hedera/NetworkVersionInfo.swift index aa2a00e7..c3706e01 100644 --- a/Sources/Hedera/NetworkVersionInfo.swift +++ b/Sources/Hedera/NetworkVersionInfo.swift @@ -21,6 +21,7 @@ import Foundation import HederaProtobufs +/// Versions of Hedera Services, and the protobuf schema. public struct NetworkVersionInfo { /// Version of the protobuf schema in use by the network. public let protobufVersion: SemanticVersion @@ -28,10 +29,16 @@ public struct NetworkVersionInfo { /// Version of the Hedera services in use by the network. public let servicesVersion: SemanticVersion + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/NodeAddress.swift b/Sources/Hedera/NodeAddress.swift index 569edd28..50ceeb6e 100644 --- a/Sources/Hedera/NodeAddress.swift +++ b/Sources/Hedera/NodeAddress.swift @@ -84,6 +84,8 @@ extension SocketAddressV4: TryProtobufCodable { } } +/// The data about a node, including its service endpoints and the Hedera account to be paid for +/// services provided by the node (that is, queries answered and transactions submitted.). public struct NodeAddress { /// A non-sequential, unique, static identifier for the node public var nodeId: UInt64 diff --git a/Sources/Hedera/NodeAddressBook.swift b/Sources/Hedera/NodeAddressBook.swift index 07f56357..80ce981e 100644 --- a/Sources/Hedera/NodeAddressBook.swift +++ b/Sources/Hedera/NodeAddressBook.swift @@ -26,10 +26,16 @@ public struct NodeAddressBook { /// all the nodes this address book contains. public let nodeAddresses: [NodeAddress] + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/PrivateKey.swift b/Sources/Hedera/PrivateKey.swift index c1f03a0b..5b6f5390 100644 --- a/Sources/Hedera/PrivateKey.swift +++ b/Sources/Hedera/PrivateKey.swift @@ -36,7 +36,7 @@ internal struct Keccak256Digest: Crypto.SecpDigest { fileprivate let inner: Data internal static let byteCount: Int = 32 - func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { + internal func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { try inner.withUnsafeBytes(body) } } @@ -423,17 +423,17 @@ public struct PrivateKey: LosslessStringConvertible, ExpressibleByStringLiteral, case .ed25519(let key): var seed = key.rawRepresentation - let i1: Int32 + let idx1: Int32 switch index { - case 0x00ff_ffff_ffff: i1 = 0xff - case 0...: i1 = 0 - default: i1 = -1 + case 0x00ff_ffff_ffff: idx1 = 0xff + case 0...: idx1 = 0 + default: idx1 = -1 } - let i2 = UInt8(truncatingIfNeeded: index) + let idx2 = UInt8(truncatingIfNeeded: index) - seed.append(i1.bigEndianBytes) - seed.append(Data([i2, i2, i2, i2])) + seed.append(idx1.bigEndianBytes) + seed.append(Data([idx2, idx2, idx2, idx2])) let salt = Data([0xff]) diff --git a/Sources/Hedera/Protobufs/FromProtobuf.swift b/Sources/Hedera/Protobufs/FromProtobuf.swift index fe0b8769..48a6d124 100644 --- a/Sources/Hedera/Protobufs/FromProtobuf.swift +++ b/Sources/Hedera/Protobufs/FromProtobuf.swift @@ -14,7 +14,14 @@ extension TryFromProtobuf { } internal init(protobufBytes bytes: Data) throws where Protobuf: SwiftProtobuf.Message { - try self.init(protobuf: try Protobuf(contiguousBytes: bytes)) + let protobuf: Protobuf + do { + protobuf = try Protobuf(contiguousBytes: bytes) + } catch { + throw HError.fromProtobuf("error decoding protobuf bytes: \(error)") + } + + try self.init(protobuf: protobuf) } } diff --git a/Sources/Hedera/PublicKey.swift b/Sources/Hedera/PublicKey.swift index 8aa99476..bc560eeb 100644 --- a/Sources/Hedera/PublicKey.swift +++ b/Sources/Hedera/PublicKey.swift @@ -41,14 +41,11 @@ public struct PublicKey: LosslessStringConvertible, ExpressibleByStringLiteral, } fileprivate var kind: PublicKey.Kind { - // swiftlint:disable force_try switch self { case .ecdsa(let key, let compressed): return .ecdsa(try! .init(rawRepresentation: key, format: compressed ? .compressed : .uncompressed)) case .ed25519(let key): return .ed25519(try! .init(rawRepresentation: key)) } - - // swiftlint:enable force_try } } @@ -205,7 +202,6 @@ public struct PublicKey: LosslessStringConvertible, ExpressibleByStringLiteral, } public init(stringLiteral value: StringLiteralType) { - // swiftlint:disable:next force_try try! self.init(parsing: value) } @@ -229,7 +225,6 @@ public struct PublicKey: LosslessStringConvertible, ExpressibleByStringLiteral, var serializer = DER.Serializer() - // swiftlint:disable:next force_try try! serializer.serialize(spki) return Data(serializer.serializedBytes) @@ -354,7 +349,7 @@ public struct PublicKey: LosslessStringConvertible, ExpressibleByStringLiteral, precondition(outputLen == output.count) } - // fixme(important): sec1 uncompressed point + // note(important): sec1 uncompressed point let hash = Crypto.Sha3.keccak256(output[1...]) return try! EvmAddress(Data(hash.dropFirst(12))) diff --git a/Sources/Hedera/Schedule/ScheduleInfo.swift b/Sources/Hedera/Schedule/ScheduleInfo.swift index 9efc12fc..9fd4e92b 100644 --- a/Sources/Hedera/Schedule/ScheduleInfo.swift +++ b/Sources/Hedera/Schedule/ScheduleInfo.swift @@ -43,7 +43,7 @@ public struct ScheduleInfo { /// it executes). public let scheduledTransactionId: TransactionId - private let scheduledTransaction: Proto_SchedulableTransactionBody + private let scheduledTransactionBody: Proto_SchedulableTransactionBody /// When set to true, the transaction will be evaluated for execution at `expiration_time` /// instead of when all required signatures are received. @@ -61,70 +61,77 @@ public struct ScheduleInfo { /// The time the schedule transaction was deleted. public let deletedAt: Timestamp? + /// Ledger ID for the network the response was returned from. public let ledgerId: LedgerId /// Returns the transaction associated with this schedule /// - /// This function may or may not be O(1) + /// >Note: This may or may not be O(1) /// - /// This function may or may not throw - /// - /// This function name is not final. - public func getScheduledTransaction() throws -> Transaction { - let transactionBody = Proto_TransactionBody.with { proto in - switch scheduledTransaction.data { - case .contractCall(let data): proto.data = .contractCall(data) - case .contractCreateInstance(let data): proto.data = .contractCreateInstance(data) - case .contractUpdateInstance(let data): proto.data = .contractUpdateInstance(data) - case .contractDeleteInstance(let data): proto.data = .contractDeleteInstance(data) - case .cryptoApproveAllowance(let data): proto.data = .cryptoApproveAllowance(data) - case .cryptoDeleteAllowance(let data): proto.data = .cryptoDeleteAllowance(data) - case .cryptoCreateAccount(let data): proto.data = .cryptoCreateAccount(data) - case .cryptoDelete(let data): proto.data = .cryptoDelete(data) - case .cryptoTransfer(let data): proto.data = .cryptoTransfer(data) - case .cryptoUpdateAccount(let data): proto.data = .cryptoUpdateAccount(data) - case .fileAppend(let data): proto.data = .fileAppend(data) - case .fileCreate(let data): proto.data = .fileCreate(data) - case .fileDelete(let data): proto.data = .fileDelete(data) - case .fileUpdate(let data): proto.data = .fileUpdate(data) - case .systemDelete(let data): proto.data = .systemDelete(data) - case .systemUndelete(let data): proto.data = .systemUndelete(data) - case .freeze(let data): proto.data = .freeze(data) - case .consensusCreateTopic(let data): proto.data = .consensusCreateTopic(data) - case .consensusUpdateTopic(let data): proto.data = .consensusUpdateTopic(data) - case .consensusDeleteTopic(let data): proto.data = .consensusDeleteTopic(data) - case .consensusSubmitMessage(let data): proto.data = .consensusSubmitMessage(data) - case .tokenCreation(let data): proto.data = .tokenCreation(data) - case .tokenFreeze(let data): proto.data = .tokenFreeze(data) - case .tokenUnfreeze(let data): proto.data = .tokenUnfreeze(data) - case .tokenGrantKyc(let data): proto.data = .tokenGrantKyc(data) - case .tokenRevokeKyc(let data): proto.data = .tokenRevokeKyc(data) - case .tokenDeletion(let data): proto.data = .tokenDeletion(data) - case .tokenUpdate(let data): proto.data = .tokenUpdate(data) - case .tokenMint(let data): proto.data = .tokenMint(data) - case .tokenBurn(let data): proto.data = .tokenBurn(data) - case .tokenWipe(let data): proto.data = .tokenWipe(data) - case .tokenAssociate(let data): proto.data = .tokenAssociate(data) - case .tokenDissociate(let data): proto.data = .tokenDissociate(data) - case .tokenFeeScheduleUpdate(let data): proto.data = .tokenFeeScheduleUpdate(data) - case .tokenPause(let data): proto.data = .tokenPause(data) - case .tokenUnpause(let data): proto.data = .tokenUnpause(data) - case .scheduleDelete(let data): proto.data = .scheduleDelete(data) - case .utilPrng(let data): proto.data = .utilPrng(data) - case nil: break + /// This may or may not throw in the future. + public var scheduledTransaction: Transaction { + get throws { + let transactionBody = Proto_TransactionBody.with { proto in + switch scheduledTransactionBody.data { + case .contractCall(let data): proto.data = .contractCall(data) + case .contractCreateInstance(let data): proto.data = .contractCreateInstance(data) + case .contractUpdateInstance(let data): proto.data = .contractUpdateInstance(data) + case .contractDeleteInstance(let data): proto.data = .contractDeleteInstance(data) + case .cryptoApproveAllowance(let data): proto.data = .cryptoApproveAllowance(data) + case .cryptoDeleteAllowance(let data): proto.data = .cryptoDeleteAllowance(data) + case .cryptoCreateAccount(let data): proto.data = .cryptoCreateAccount(data) + case .cryptoDelete(let data): proto.data = .cryptoDelete(data) + case .cryptoTransfer(let data): proto.data = .cryptoTransfer(data) + case .cryptoUpdateAccount(let data): proto.data = .cryptoUpdateAccount(data) + case .fileAppend(let data): proto.data = .fileAppend(data) + case .fileCreate(let data): proto.data = .fileCreate(data) + case .fileDelete(let data): proto.data = .fileDelete(data) + case .fileUpdate(let data): proto.data = .fileUpdate(data) + case .systemDelete(let data): proto.data = .systemDelete(data) + case .systemUndelete(let data): proto.data = .systemUndelete(data) + case .freeze(let data): proto.data = .freeze(data) + case .consensusCreateTopic(let data): proto.data = .consensusCreateTopic(data) + case .consensusUpdateTopic(let data): proto.data = .consensusUpdateTopic(data) + case .consensusDeleteTopic(let data): proto.data = .consensusDeleteTopic(data) + case .consensusSubmitMessage(let data): proto.data = .consensusSubmitMessage(data) + case .tokenCreation(let data): proto.data = .tokenCreation(data) + case .tokenFreeze(let data): proto.data = .tokenFreeze(data) + case .tokenUnfreeze(let data): proto.data = .tokenUnfreeze(data) + case .tokenGrantKyc(let data): proto.data = .tokenGrantKyc(data) + case .tokenRevokeKyc(let data): proto.data = .tokenRevokeKyc(data) + case .tokenDeletion(let data): proto.data = .tokenDeletion(data) + case .tokenUpdate(let data): proto.data = .tokenUpdate(data) + case .tokenMint(let data): proto.data = .tokenMint(data) + case .tokenBurn(let data): proto.data = .tokenBurn(data) + case .tokenWipe(let data): proto.data = .tokenWipe(data) + case .tokenAssociate(let data): proto.data = .tokenAssociate(data) + case .tokenDissociate(let data): proto.data = .tokenDissociate(data) + case .tokenFeeScheduleUpdate(let data): proto.data = .tokenFeeScheduleUpdate(data) + case .tokenPause(let data): proto.data = .tokenPause(data) + case .tokenUnpause(let data): proto.data = .tokenUnpause(data) + case .scheduleDelete(let data): proto.data = .scheduleDelete(data) + case .utilPrng(let data): proto.data = .utilPrng(data) + case nil: break + } + + proto.memo = scheduledTransactionBody.memo + proto.transactionFee = scheduledTransactionBody.transactionFee } - proto.memo = scheduledTransaction.memo - proto.transactionFee = scheduledTransaction.transactionFee + return try AnyTransaction.fromProtobuf(transactionBody, [transactionBody.data!]).transaction } - - return try AnyTransaction.fromProtobuf(transactionBody, [transactionBody.data!]).transaction } + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } @@ -156,7 +163,7 @@ extension ScheduleInfo: TryProtobufCodable { signatories: try .fromProtobuf(proto.signers), adminKey: proto.hasAdminKey ? try .fromProtobuf(proto.adminKey) : nil, scheduledTransactionId: try .fromProtobuf(proto.scheduledTransactionID), - scheduledTransaction: proto.scheduledTransactionBody, + scheduledTransactionBody: proto.scheduledTransactionBody, waitForExpiry: proto.waitForExpiry, memo: proto.memo, expirationTime: proto.hasExpirationTime ? .fromProtobuf(proto.expirationTime) : nil, @@ -201,7 +208,7 @@ extension ScheduleInfo: TryProtobufCodable { proto.deletionTime = deletedAt.toProtobuf() } - proto.scheduledTransactionBody = scheduledTransaction + proto.scheduledTransactionBody = scheduledTransactionBody } } } diff --git a/Sources/Hedera/StakingInfo.swift b/Sources/Hedera/StakingInfo.swift index 75a0469c..344aaed3 100644 --- a/Sources/Hedera/StakingInfo.swift +++ b/Sources/Hedera/StakingInfo.swift @@ -21,6 +21,7 @@ import Foundation import HederaProtobufs +/// Info related to account/contract staking settings. public struct StakingInfo { /// If true, the contract declines receiving a staking reward. The default value is false. public let declineStakingReward: Bool @@ -43,10 +44,16 @@ public struct StakingInfo { /// The ID of the node this account or contract is staked to. public let stakedNodeId: UInt64? + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/Token/TokenAssociation.swift b/Sources/Hedera/Token/TokenAssociation.swift index 61638916..1d34209c 100644 --- a/Sources/Hedera/Token/TokenAssociation.swift +++ b/Sources/Hedera/Token/TokenAssociation.swift @@ -29,6 +29,7 @@ public struct TokenAssociation { /// The account involved in the association. public let accountId: AccountId + /// Convert self to protobuf encoded bytes. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/Token/TokenInfo.swift b/Sources/Hedera/Token/TokenInfo.swift index facff36b..845aaadb 100644 --- a/Sources/Hedera/Token/TokenInfo.swift +++ b/Sources/Hedera/Token/TokenInfo.swift @@ -21,6 +21,7 @@ import Foundation import HederaProtobufs +/// Response from ``TokenInfoQuery``. public final class TokenInfo { internal init( tokenId: TokenId, @@ -157,12 +158,18 @@ public final class TokenInfo { /// The ledger ID the response was returned from public let ledgerId: LedgerId + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { - self.toProtobufBytes() + toProtobufBytes() } } diff --git a/Sources/Hedera/Token/TokenNftInfo.swift b/Sources/Hedera/Token/TokenNftInfo.swift index ccdfc2eb..b2ec1318 100644 --- a/Sources/Hedera/Token/TokenNftInfo.swift +++ b/Sources/Hedera/Token/TokenNftInfo.swift @@ -57,10 +57,16 @@ public final class TokenNftInfo { self.ledgerId = ledgerId } + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/Token/TokenSupplyType.swift b/Sources/Hedera/Token/TokenSupplyType.swift index 04ba02aa..5650994f 100644 --- a/Sources/Hedera/Token/TokenSupplyType.swift +++ b/Sources/Hedera/Token/TokenSupplyType.swift @@ -23,9 +23,12 @@ import SwiftProtobuf /// Possible token supply types. /// Can be used to restrict supply to a set maximum. -/// Defaults to `infinite`. +/// +/// Defaults to ``infinite``. public enum TokenSupplyType { + /// Indicates the token has a maximum supply of `UInt64.max`. case infinite + /// Indicates the token has a configurable maximum supply, provided on token creation. case finite } diff --git a/Sources/Hedera/Token/TokenType.swift b/Sources/Hedera/Token/TokenType.swift index 9eba948a..81b2cb82 100644 --- a/Sources/Hedera/Token/TokenType.swift +++ b/Sources/Hedera/Token/TokenType.swift @@ -29,9 +29,16 @@ import SwiftProtobuf /// /// Only `fungibleCommon` and `nonFungibleUnique` are supported right now. More /// may be added in the future. -/// public enum TokenType { + /// Tokens are interchangeable value with one another, where any quantity of them has the same value as + /// another equal quantity if they are in the same class. Share a single set of properties, not + /// distinct from one another. Simply represented as a balance or quantity to a given Hedera + /// account. case fungibleCommon + + /// Tokens are unique, + /// not interchangeable with other tokens of the same type as they typically have different values. + /// Individually traced and can carry unique properties (e.g. serial number). case nonFungibleUnique } diff --git a/Sources/Hedera/Topic/TopicInfo.swift b/Sources/Hedera/Topic/TopicInfo.swift index c6fc39db..fca262dd 100644 --- a/Sources/Hedera/Topic/TopicInfo.swift +++ b/Sources/Hedera/Topic/TopicInfo.swift @@ -54,10 +54,16 @@ public struct TopicInfo { /// The ledger ID the response was returned from public let ledgerId: LedgerId + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/Transaction/TransactionSources.swift b/Sources/Hedera/Transaction/TransactionSources.swift index 4eabe9ef..ea0d7937 100644 --- a/Sources/Hedera/Transaction/TransactionSources.swift +++ b/Sources/Hedera/Transaction/TransactionSources.swift @@ -177,6 +177,8 @@ extension TransactionSources { } } + // it won't fit on the same line. + // swiftlint:disable closure_parameter_position let transactionInfo = try signedTransactions.map { signedTx -> (transactionId: TransactionId, nodeAccountId: AccountId) in let transactionBody: Proto_TransactionBody @@ -192,6 +194,8 @@ extension TransactionSources { return (transactionId: transactionId, nodeAccountId: nodeAccountId) } + // swiftlint:enable closure_parameter_position + let chunks: [Range] let transactionIds: [TransactionId] let nodeAccountIds: [AccountId] diff --git a/Sources/Hedera/TransactionReceipt.swift b/Sources/Hedera/TransactionReceipt.swift index bcd9059f..b42e97a5 100644 --- a/Sources/Hedera/TransactionReceipt.swift +++ b/Sources/Hedera/TransactionReceipt.swift @@ -156,9 +156,10 @@ public struct TransactionReceipt { ) } + /// @discardableResult public func validateStatus(_ doValidate: Bool) throws -> Self { - if doValidate && status != Status.ok { + if doValidate && status != .ok { throw HError( kind: .receiptStatus(status: status, transactionId: transactionId), description: @@ -169,10 +170,16 @@ public struct TransactionReceipt { return self } + /// Decode `Self` from protobuf-encoded `bytes`. + /// + /// - Throws: ``HError/ErrorKind/fromProtobuf`` if: + /// decoding the bytes fails to produce a valid protobuf, or + /// decoding the protobuf fails. public static func fromBytes(_ bytes: Data) throws -> Self { try Self(protobufBytes: bytes) } + /// Convert `self` to protobuf encoded data. public func toBytes() -> Data { toProtobufBytes() } diff --git a/Sources/Hedera/TransactionResponse.swift b/Sources/Hedera/TransactionResponse.swift index 3a7ee361..1b009238 100644 --- a/Sources/Hedera/TransactionResponse.swift +++ b/Sources/Hedera/TransactionResponse.swift @@ -42,8 +42,10 @@ public struct TransactionResponse { /// This can be used to lookup the transaction in an explorer. public let transactionHash: TransactionHash + /// Whether the receipt/record status should be validated. public var validateStatus: Bool = true + /// Whether the receipt/record status should be validated. @discardableResult public mutating func validateStatus(_ validateStatus: Bool) -> Self { self.validateStatus = validateStatus @@ -51,13 +53,16 @@ public struct TransactionResponse { return self } - /// Get the receipt of this transaction. + /// Queries the receipt for the associated transaction. + /// /// Will wait for consensus. - /// Will return a `receiptStatus` error for a failing receipt. + /// + /// - Throws: an error of type ``HError``. public func getReceipt(_ client: Client, _ timeout: TimeInterval? = nil) async throws -> TransactionReceipt { try await getReceiptQuery().execute(client, timeout) } + /// Returns a query that when executed, returns the receipt for the associated transaction. public func getReceiptQuery() -> TransactionReceiptQuery { TransactionReceiptQuery() .transactionId(transactionId) @@ -65,10 +70,16 @@ public struct TransactionResponse { .validateStatus(validateStatus) } + /// Get the record for the associated transaction. + /// + /// Will wait for consensus. + /// + /// - Throws: an error of type ``HError``. public func getRecord(_ client: Client, _ timeout: TimeInterval? = nil) async throws -> TransactionRecord { try await getRecordQuery().execute(client, timeout) } + /// Returns a query that when executed, returns the record for the associated transaction. public func getRecordQuery() -> TransactionRecordQuery { TransactionRecordQuery() .transactionId(transactionId) diff --git a/Sources/Hedera/TransferTransaction.swift b/Sources/Hedera/TransferTransaction.swift index ba297983..8e3a8cf0 100644 --- a/Sources/Hedera/TransferTransaction.swift +++ b/Sources/Hedera/TransferTransaction.swift @@ -93,14 +93,14 @@ public final class TransferTransaction: Transaction { /// Add a non-approved hbar transfer to the transaction. @discardableResult - public func hbarTransfer(_ accountId: AccountId, _ amount: Int64) -> Self { - doHbarTransfer(accountId, amount, false) + public func hbarTransfer(_ accountId: AccountId, _ amount: Hbar) -> Self { + doHbarTransfer(accountId, amount.toTinybars(), false) } /// Add an approved hbar transfer to the transaction. @discardableResult - public func approvedHbarTransfer(_ accountId: AccountId, _ amount: Int64) -> Self { - doHbarTransfer(accountId, amount, true) + public func approvedHbarTransfer(_ accountId: AccountId, _ amount: Hbar) -> Self { + doHbarTransfer(accountId, amount.toTinybars(), true) } /// Add a non-approved token transfer to the transaction. diff --git a/Taskfile.yml b/Taskfile.yml index ecb29730..d29b332a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,10 +1,5 @@ version: "3" -includes: - ":rust": - taskfile: ../rust/Taskfile.yml - dir: ../rust - tasks: build: cmds: diff --git a/Tests/HederaTests/HbarTests.swift b/Tests/HederaTests/HbarTests.swift index 898cd436..4ba2fedd 100644 --- a/Tests/HederaTests/HbarTests.swift +++ b/Tests/HederaTests/HbarTests.swift @@ -90,7 +90,7 @@ internal final class HbarTests: XCTestCase { internal func testTo() { let twentyTwoKilobars: Hbar = 22_000 - XCTAssertEqual(twentyTwoKilobars.getValue(), 22_000) + XCTAssertEqual(twentyTwoKilobars.value, 22_000) XCTAssertEqual(twentyTwoKilobars.to(.tinybar), 2_200_000_000_000) XCTAssertEqual(twentyTwoKilobars.to(.microbar), 22_000_000_000) XCTAssertEqual(twentyTwoKilobars.to(.millibar), 22_000_000) diff --git a/Tests/HederaTests/RlpTests.swift b/Tests/HederaTests/RlpTests.swift index aba4e36b..9d45d801 100644 --- a/Tests/HederaTests/RlpTests.swift +++ b/Tests/HederaTests/RlpTests.swift @@ -8,9 +8,10 @@ import XCTest // adapted from https://github.com/ethereum/tests/blob/d25a79ae508daeb60bee0bf819ac7e884fc494d7/RLPTests/rlptest.json // which is licensed under the MIT license. private struct Test { - fileprivate init(_ a: Input, _ b: Output) { - self.a = a - self.b = b + + fileprivate init(_ left: Input, _ right: Output) { + self.left = left + self.right = right } fileprivate enum Input: ExpressibleByArrayLiteral, ExpressibleByStringLiteral, Equatable { @@ -32,9 +33,8 @@ private struct Test { data = Data(hexEncoded: value.stripPrefix("0x") ?? value[...])! } } - - fileprivate let a: Input - fileprivate let b: Output + fileprivate let left: Input + fileprivate let right: Output } extension Test.Input: RlpDecodable, RlpEncodable { @@ -46,7 +46,7 @@ extension Test.Input: RlpDecodable, RlpEncodable { } } - func encode(to encoder: inout Rlp.Encoder) { + fileprivate func encode(to encoder: inout Rlp.Encoder) { switch self { case .value(let value): encoder.append(value.data(using: .utf8)!) @@ -167,13 +167,13 @@ public final class RlpTests: XCTestCase { } private func encodeTest(_ test: Test) { - XCTAssertEqual(test.a.rlpEncoded().hexStringEncoded(), test.b.data.hexStringEncoded()) + XCTAssertEqual(test.left.rlpEncoded().hexStringEncoded(), test.right.data.hexStringEncoded()) } private func decodeTest(_ test: Test) throws { - let decoded = try Self.decode(test.b.data) + let decoded = try Self.decode(test.right.data) - XCTAssertEqual(decoded, test.a) + XCTAssertEqual(decoded, test.left) } internal func testEncodeEmptyString() {