Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FlipcashAPI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ let contractDependencies: [Package.Dependency] = protoLocalRoot.map { root in
.package(path: "\(root)/flipcash2-client-protocol"),
]
} ?? [
.package(url: "https://github.com/code-payments/ocp-client-protocol", exact: "0.1.0"),
.package(url: "https://github.com/code-payments/ocp-client-protocol", exact: "0.2.0"),
.package(url: "https://github.com/code-payments/flipcash2-client-protocol", exact: "0.1.0"),
]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Client+Balance.swift
// FlipcashCore
//

import Foundation
import FlipcashAPI

extension Client {
/// Fetches the core-mint balance for any owner account. No signature is
/// required — the server allows reading balance for any owner.
public func getBalance(owner: PublicKey) async throws -> TokenAmount {
try await withCheckedThrowingContinuation { c in
balanceService.getBalance(owner: owner) { c.resume(with: $0) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class Client: ObservableObject {
internal let transactionService: TransactionService
internal let currencyService: CurrencyService
internal let messagingService: MessagingService
internal let balanceService: BalanceService

// MARK: - Init -

Expand All @@ -53,6 +54,7 @@ public class Client: ObservableObject {
self.transactionService = TransactionService(client: client)
self.currencyService = CurrencyService(client: client)
self.messagingService = MessagingService(client: client)
self.balanceService = BalanceService(client: client)
}

deinit {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// BalanceService.swift
// FlipcashCore
//

import Foundation
import FlipcashAPI
import GRPCCore

private let logger = Logger(label: "flipcash.balance-service")

final class BalanceService: Sendable {

private let service: Ocp_Balance_V1_Balance.Client<AppTransport>

init(client: GRPCClient<AppTransport>) {
self.service = Ocp_Balance_V1_Balance.Client(wrapping: client)
}

/// Fetches the core-mint balance for any owner account. Unlike every other
/// Payments API request, `GetBalanceRequest` carries no auth/signature field —
/// the server allows reading balance for any owner, not just the caller's own,
/// so this takes a bare `PublicKey` rather than a signing `KeyPair`.
func getBalance(owner: PublicKey, completion: @Sendable @escaping (Result<TokenAmount, ErrorGetBalance>) -> Void) {
logger.info("Fetching balance")

let request = Ocp_Balance_V1_GetBalanceRequest.with {
$0.owner = owner.solanaAccountID
}

Task {
do {
let response = try await service.getBalance(request, options: .unaryDefault)
let error = ErrorGetBalance(rawValue: response.result.rawValue) ?? .unknown
guard error == .ok else {
logger.error("Failed to fetch balance", metadata: ["error": "\(error)"])
await MainActor.run { completion(.failure(error)) }
return
}
let balance = TokenAmount(quarks: response.coreMintValue, mint: .usdf)
await MainActor.run { completion(.success(balance)) }
} catch let error as RPCError {
await MainActor.run { completion(.failure(.from(transportError: error))) }
} catch {
await MainActor.run { completion(.failure(.unknown)) }
}
}
}
}

// MARK: - Errors -

public enum ErrorGetBalance: Int, Error, Equatable, Sendable {
case ok
case denied
case notFound
case unknown = -1
case transportFailure = -2
case cancelled = -3
case rejected = -4
}

extension ErrorGetBalance: ServerError, TransportClassifiableError {
public var reportingLevel: ErrorReportingLevel {
switch self {
case .ok, .transportFailure: .suppressed
case .cancelled: .info
case .denied, .notFound: .info
case .unknown, .rejected: .error
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct TransportClassificationTests {
// MARK: - Registry (one line per TransportClassifiableError conformer) -

@Test func errorFetchBalance() { assertClassifies(ErrorFetchBalance.self) }
@Test func errorGetBalance() { assertClassifies(ErrorGetBalance.self) }

@Test func errorRegisterAccount() { assertClassifies(ErrorRegisterAccount.self) }
@Test func errorLoginAccount() { assertClassifies(ErrorLoginAccount.self) }
Expand Down