Skip to content

Commit

Permalink
Implement Hbar type
Browse files Browse the repository at this point in the history
  • Loading branch information
qtbeee committed Jan 17, 2020
1 parent 29c01b6 commit c8d5f76
Show file tree
Hide file tree
Showing 16 changed files with 228 additions and 80 deletions.
11 changes: 6 additions & 5 deletions Sources/Hedera/Client.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Sodium
import GRPC
import NIO
import Foundation

struct Node {
let accountId: AccountId
Expand All @@ -21,9 +22,9 @@ typealias HederaGRPCClient = (fileService: Proto_FileServiceServiceClient,
cryptoService: Proto_CryptoServiceServiceClient,
contractService: Proto_SmartContractServiceServiceClient)

let defaultMaxTransactionFee: UInt64 = 100_000_000 // 1h
let defaultMaxTransactionFee = Hbar(hbar: Decimal(1))! // 1h

let defaultMaxQueryPayment: UInt64 = 100_000_000 // 1h
let defaultMaxQueryPayment = Hbar(hbar: Decimal(1))! // 1h

public class Client {
var `operator`: Operator?
Expand Down Expand Up @@ -93,11 +94,11 @@ public class Client {
/// This can be overridden for an individual transaction with `.setTransactionFee()`.
///
/// - Parameters:
/// - max: The maximum transaction fee, in tinybars.
/// - max: The maximum transaction fee.
///
/// - Returns: Self for fluent usage.
@discardableResult
public func setMaxTransactionFee(_ max: UInt64) -> Self {
public func setMaxTransactionFee(_ max: Hbar) -> Self {
maxTransactionFee = max
return self
}
Expand All @@ -110,7 +111,7 @@ public class Client {
///
/// - Returns: Self for fluent usage.
@discardableResult
public func setMaxQueryPayment(_ max: UInt64) -> Self {
public func setMaxQueryPayment(_ max: Hbar) -> Self {
maxQueryPayment = max
return self
}
Expand Down
66 changes: 66 additions & 0 deletions Sources/Hedera/Hbar.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import Foundation

public struct Hbar {
private let tinybar: Int64

private init(tinybar: Int64) {
self.tinybar = tinybar
}

public static let MAX = Hbar(tinybar: Int64.max)
public static let MIN = Hbar(tinybar: Int64.min)
public static let ZERO = Hbar(tinybar: 0)

public init?(hbar: Decimal) {
let tinybarAmount = hbar * HbarUnit.Hbar.toTinybarCount()
guard let tinybar = Int64(exactly: NSDecimalNumber(decimal: tinybarAmount)) else { return nil }

self.init(tinybar: tinybar)
}

public static func from(amount: Decimal, unit: HbarUnit) -> Self? {
guard let tinybar = Int64(exactly: NSDecimalNumber(decimal: amount * unit.toTinybarCount())) else { return nil }

return Self(tinybar: tinybar)
}

public static func from(amount: Int64, unit: HbarUnit) -> Self? {
guard let tinybar = Int64(exactly: NSDecimalNumber(decimal: Decimal(amount) * unit.toTinybarCount())) else { return nil }

return Self(tinybar: tinybar)
}

public static func fromTinybar(amount: Int64) -> Self {
Self(tinybar: amount)
}

public func `as`(unit: HbarUnit) -> Decimal {
if unit == .Tinybar {
return Decimal(tinybar)
}
return Decimal(tinybar) / unit.toTinybarCount()
}

public func asTinybar() -> Int64 {
return tinybar
}
}

extension Hbar: CustomStringConvertible {
public var description: String {
if Decimal(tinybar) < HbarUnit.Hbar.toTinybarCount() {
return "\(tinybar) \(HbarUnit.Tinybar.getSymbol)"
}
return "\(Decimal(tinybar) / HbarUnit.Hbar.toTinybarCount()) \(HbarUnit.Hbar.getSymbol) (\(tinybar) tinybar)"
}
}

extension Hbar: Comparable {
public static func < (lhs: Hbar, rhs: Hbar) -> Bool {
return lhs.tinybar < rhs.tinybar
}

public static func == (lhs: Hbar, rhs: Hbar) -> Bool {
return lhs.tinybar == rhs.tinybar
}
}
73 changes: 73 additions & 0 deletions Sources/Hedera/HbarUnit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Foundation

public enum HbarUnit {
case Tinybar
case Microbar
case Millibar
case Hbar
case Kilobar
case Megabar
case Gigabar

public var getSymbol: String {
switch self {
case .Tinybar:
return "tℏ"
case .Microbar:
return "μℏ"
case .Millibar:
return "mℏ"
case .Hbar:
return ""
case .Kilobar:
return "kℏ"
case .Megabar:
return "Mℏ"
case .Gigabar:
return "Gℏ"
}
}

func toTinybarCount() -> Decimal {
let amount: UInt64
switch self {
case .Tinybar:
amount = 1
case .Microbar:
amount = 100
case .Millibar:
amount = 100_000
case .Hbar:
amount = 100_000_000
case .Kilobar:
amount = 100_000_000 * 1_000
case .Megabar:
amount = 100_000_000 * 1_000_000
case .Gigabar:
amount = 100_000_000 * 1_000_000_000
}

return Decimal(amount)
}
}

extension HbarUnit: CustomStringConvertible {
public var description: String {
switch self {
case .Tinybar:
return "tinybar"
case .Microbar:
return "microbar"
case .Millibar:
return "millibar"
case .Hbar:
return "hbar"
case .Kilobar:
return "kilobar"
case .Megabar:
return "megabar"
case .Gigabar:
return "gigabar"
}
}
}
23 changes: 12 additions & 11 deletions Sources/Hedera/QueryBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ public class QueryBuilder<R> {
// var header = Proto_QueryHeader()
var needsPayment: Bool { true }

var maxQueryPayment: UInt64?
var queryPayment: UInt64?
var maxQueryPayment: Hbar?
var queryPayment: Hbar?

init() {}

@discardableResult
public func setMaxQueryPayment(_ max: UInt64) -> Self {
public func setMaxQueryPayment(_ max: Hbar) -> Self {
maxQueryPayment = max
return self
}

@discardableResult
public func setQueryPayment(_ amount: UInt64) -> Self {
public func setQueryPayment(_ amount: Hbar) -> Self {
queryPayment = amount
return self
}
Expand All @@ -37,7 +37,7 @@ public class QueryBuilder<R> {
}

@discardableResult
func getCost(client: Client, node: Node) -> EventLoopFuture<UInt64> {
func getCost(client: Client, node: Node) -> EventLoopFuture<Hbar> {
// Store the current response type and payment
let responseType = withHeader { $0.responseType }
let payment = withHeader { $0.payment }
Expand All @@ -57,14 +57,15 @@ public class QueryBuilder<R> {

// COST_ANSWER requires a 0 payment and does not actually process it
$0.payment = CryptoTransferTransaction()
.addSender(client.operator!.id, amount: 0)
.addRecipient(node.accountId, amount: 0)
.addSender(client.operator!.id, amount: Hbar.ZERO)
.addRecipient(node.accountId, amount: Hbar.ZERO)
.build(client: client)
.addSigPair(publicKey: client.operator!.publicKey, signer: client.operator!.signer)
.toProto()
}

let eventLoop = client.eventLoopGroup.next()
let successValueMapper = { Hbar.fromTinybar(amount: Int64(self.getResponseHeader($0).cost)) }

return self.getQueryMethod(client.grpcClient(for: node))(self.body, nil)
.response
Expand All @@ -79,11 +80,11 @@ public class QueryBuilder<R> {
node: node,
startTime: Date(),
attempt: 0,
successValueMapper: { self.getResponseHeader($0).cost }
successValueMapper: successValueMapper
)
}

switch resultFromCode(code, success: { header.cost }) {
switch resultFromCode(code, success: { successValueMapper(resp) }) {
case .success(let res):
return eventLoop.makeSucceededFuture(res)

Expand All @@ -94,7 +95,7 @@ public class QueryBuilder<R> {
}

@discardableResult
public func getCost(client: Client) -> EventLoopFuture<UInt64> {
public func getCost(client: Client) -> EventLoopFuture<Hbar> {
return getCost(client: client, node: client.pickNode())
}

Expand Down Expand Up @@ -162,7 +163,7 @@ public class QueryBuilder<R> {
}
}

func generateQueryPaymentTransaction(client: Client, node: Node, amount: UInt64) {
func generateQueryPaymentTransaction(client: Client, node: Node, amount: Hbar) {
let tx = CryptoTransferTransaction()
.setNodeAccountId(node.accountId)
.addSender(client.operator!.id, amount: amount)
Expand Down
6 changes: 3 additions & 3 deletions Sources/Hedera/TransactionBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public class TransactionBuilder {
}

@discardableResult
public func setMaxTransactionFee(_ fee: UInt64) -> Self {
body.transactionFee = fee
public func setMaxTransactionFee(_ fee: Hbar) -> Self {
body.transactionFee = UInt64(fee.asTinybar())

return self
}
Expand Down Expand Up @@ -50,7 +50,7 @@ public class TransactionBuilder {
// If we have a client, set some defaults if they have not already been set
if let client = client {
if body.transactionFee == 0 {
body.transactionFee = client.maxTransactionFee
setMaxTransactionFee(client.maxTransactionFee)
}

if !body.hasNodeAccountID {
Expand Down
26 changes: 4 additions & 22 deletions Sources/Hedera/TransactionRecord.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Foundation
public struct TransactionRecord {
public let transactionId: TransactionId
public let transactionHash: Bytes
public let transactionFee: UInt64
public let transactionFee: Hbar
public let consensusTimestamp: Date?
public let transactionMemo: String?
public let receipt: TransactionReceipt
Expand All @@ -18,7 +18,7 @@ public struct TransactionRecord {

transactionId = TransactionId(proto.transactionID)!
transactionHash = Bytes(proto.transactionHash)
transactionFee = UInt64(proto.transactionFee)
transactionFee = Hbar.fromTinybar(amount: Int64(proto.transactionFee))
consensusTimestamp = proto.hasConsensusTimestamp ? Date(proto.consensusTimestamp): nil
transactionMemo = proto.memo.isEmpty ? nil : proto.memo
receipt = TransactionReceipt(proto.receipt)
Expand All @@ -29,28 +29,10 @@ public struct TransactionRecord {

public struct Transfer {
public let accountId: AccountId
public let amount: UInt64
public let amount: Hbar

init(_ proto: Proto_AccountAmount) {
accountId = AccountId(proto.accountID)
amount = UInt64(proto.amount)
amount = Hbar.fromTinybar(amount: proto.amount)
}
}

// public enum ContractResultType {
// case call(FunctionResult)
// case create(FunctionResult)

// init?(_ body: Proto_TransactionRecord.OneOf_Body?) {
// if let body = body {
// switch body {
// case .contractCallResult(let result):
// self = ContractResultType.call(FunctionResult(result))
// case .contractCreateResult(let result):
// self = ContractResultType.create(FunctionResult(result))
// }
// } else {
// return nil
// }
// }
// }
6 changes: 3 additions & 3 deletions Sources/Hedera/account/AccountBalanceQuery.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
public class AccountBalanceQuery: QueryBuilder<UInt64> {
public class AccountBalanceQuery: QueryBuilder<Hbar> {
public override init() {
super.init()

Expand All @@ -15,11 +15,11 @@ public class AccountBalanceQuery: QueryBuilder<UInt64> {
callback(&body.cryptogetAccountBalance.header)
}

override func mapResponse(_ response: Proto_Response) -> UInt64 {
override func mapResponse(_ response: Proto_Response) -> Hbar {
guard case .cryptogetAccountBalance(let response) = response.response else {
fatalError("unreachable: response is not cryptogetAccountBalance")
}

return response.balance
return Hbar.fromTinybar(amount: Int64(response.balance))
}
}
Loading

0 comments on commit c8d5f76

Please sign in to comment.