diff --git a/CHANGELOG.md b/CHANGELOG.md index 54f9bb5..9da7296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,48 @@ called out explicitly even when nothing else did. release notes, so a version with no entry here does not release. Write the entry in the same PR that syncs the contract, while the diff is still in front of you. +## 0.4.0 + +Synced to [`ocp-protobuf-api@82202912`](https://github.com/code-payments/ocp-protobuf-api/commit/82202912574e122bba90025fe8b292d5a3f04c05). + +**Breaking.** `ocp.balance.v1.Balance` had exactly one RPC and it has been replaced, so every +consumer of the service has work to do. There is no deprecation window: `GetBalance` is gone in the +same release that adds `GetBalances`. + +### Removed + +- `GetBalance`, along with `GetBalanceRequest` and `GetBalanceResponse`. + +- `GetBalancesResponse.Result.NOT_FOUND`. `OK` and `DENIED` keep `0` and `1`, so no surviving case + renumbers and no positional mapping shifts underneath you. What changes is that "this owner has no + balance" no longer has a result code: an owner with nothing to report is simply absent from + `balances_by_owner`. Code that branched on `NOT_FOUND` needs to branch on a missing map entry + instead, and code that treated a non-`OK` result as a hard failure will now see `OK` where it used + to see `NOT_FOUND`. + +### Added + +- `GetBalances`, a unary RPC that batches what `GetBalance` did one owner at a time. + + `GetBalancesRequest` takes `repeated owners` (1 to 1024) where the old request took a single + `owner`, plus an optional `repeated mints` filter (up to 1024). Leaving `mints` empty returns + every mint each owner holds, which is the closest thing to the old behaviour. + + `GetBalancesResponse` returns `map balances_by_owner`, keyed by owner + address. Each `OwnerBalance` carries the `core_mint_value` total in quarks that the old flat + response returned directly, plus `map balances_by_mint` keyed by mint + address for the per-mint breakdown. So the scalar total still exists, one level further down. + + Like `GetBalance` before it, `GetBalancesRequest` carries no auth or signature field. It reads + balances for arbitrary owner accounts rather than the caller's own, so there is nothing to sign. + +### Migrating + +A single-owner call maps across mechanically: wrap the owner in `owners`, leave `mints` empty, and +read `balances_by_owner[owner]?.core_mint_value` where you read `core_mint_value` before. Treat a +missing entry as the old `NOT_FOUND`. The per-mint breakdown and the multi-owner batch are new +capability, not something the old shape expressed, so nothing forces you to use either. + ## 0.3.0 No contract change. `ocp.lock` points at the same upstream commit as `0.2.0`, and the generated diff --git a/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.grpc.swift b/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.grpc.swift index 4af52f7..a337431 100644 --- a/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.grpc.swift +++ b/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.grpc.swift @@ -20,21 +20,21 @@ public enum Ocp_Balance_V1_Balance { public static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "ocp.balance.v1.Balance") /// Namespace for method metadata. public enum Method { - /// Namespace for "GetBalance" metadata. - public enum GetBalance { - /// Request type for "GetBalance". - public typealias Input = Ocp_Balance_V1_GetBalanceRequest - /// Response type for "GetBalance". - public typealias Output = Ocp_Balance_V1_GetBalanceResponse - /// Descriptor for "GetBalance". + /// Namespace for "GetBalances" metadata. + public enum GetBalances { + /// Request type for "GetBalances". + public typealias Input = Ocp_Balance_V1_GetBalancesRequest + /// Response type for "GetBalances". + public typealias Output = Ocp_Balance_V1_GetBalancesResponse + /// Descriptor for "GetBalances". public static let descriptor = GRPCCore.MethodDescriptor( service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "ocp.balance.v1.Balance"), - method: "GetBalance" + method: "GetBalances" ) } /// Descriptors for all methods in the "ocp.balance.v1.Balance" service. public static let descriptors: [GRPCCore.MethodDescriptor] = [ - GetBalance.descriptor + GetBalances.descriptor ] } } @@ -54,27 +54,28 @@ extension Ocp_Balance_V1_Balance { /// You don't need to implement this protocol directly, use the generated /// implementation, ``Client``. public protocol ClientProtocol: Sendable { - /// Call the "GetBalance" method. + /// Call the "GetBalances" method. /// /// > Source IDL Documentation: /// > - /// > GetBalance returns balance data for any owner account + /// > GetBalances returns balance data for a set of owner accounts, optionally + /// > filtered by a set of mints /// /// - Parameters: - /// - request: A request containing a single `Ocp_Balance_V1_GetBalanceRequest` message. - /// - serializer: A serializer for `Ocp_Balance_V1_GetBalanceRequest` messages. - /// - deserializer: A deserializer for `Ocp_Balance_V1_GetBalanceResponse` messages. + /// - request: A request containing a single `Ocp_Balance_V1_GetBalancesRequest` message. + /// - serializer: A serializer for `Ocp_Balance_V1_GetBalancesRequest` messages. + /// - deserializer: A deserializer for `Ocp_Balance_V1_GetBalancesResponse` messages. /// - options: Options to apply to this RPC. /// - handleResponse: A closure which handles the response, the result of which is /// returned to the caller. Returning from the closure will cancel the RPC if it /// hasn't already finished. /// - Returns: The result of `handleResponse`. - func getBalance( - request: GRPCCore.ClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, + func getBalances( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result ) async throws -> Result where Result: Sendable } @@ -94,33 +95,34 @@ extension Ocp_Balance_V1_Balance { self.client = client } - /// Call the "GetBalance" method. + /// Call the "GetBalances" method. /// /// > Source IDL Documentation: /// > - /// > GetBalance returns balance data for any owner account + /// > GetBalances returns balance data for a set of owner accounts, optionally + /// > filtered by a set of mints /// /// - Parameters: - /// - request: A request containing a single `Ocp_Balance_V1_GetBalanceRequest` message. - /// - serializer: A serializer for `Ocp_Balance_V1_GetBalanceRequest` messages. - /// - deserializer: A deserializer for `Ocp_Balance_V1_GetBalanceResponse` messages. + /// - request: A request containing a single `Ocp_Balance_V1_GetBalancesRequest` message. + /// - serializer: A serializer for `Ocp_Balance_V1_GetBalancesRequest` messages. + /// - deserializer: A deserializer for `Ocp_Balance_V1_GetBalancesResponse` messages. /// - options: Options to apply to this RPC. /// - handleResponse: A closure which handles the response, the result of which is /// returned to the caller. Returning from the closure will cancel the RPC if it /// hasn't already finished. /// - Returns: The result of `handleResponse`. - public func getBalance( - request: GRPCCore.ClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, + public func getBalances( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions = .defaults, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in try response.message } ) async throws -> Result where Result: Sendable { try await self.client.unary( request: request, - descriptor: Ocp_Balance_V1_Balance.Method.GetBalance.descriptor, + descriptor: Ocp_Balance_V1_Balance.Method.GetBalances.descriptor, serializer: serializer, deserializer: deserializer, options: options, @@ -133,30 +135,31 @@ extension Ocp_Balance_V1_Balance { // Helpers providing default arguments to 'ClientProtocol' methods. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension Ocp_Balance_V1_Balance.ClientProtocol { - /// Call the "GetBalance" method. + /// Call the "GetBalances" method. /// /// > Source IDL Documentation: /// > - /// > GetBalance returns balance data for any owner account + /// > GetBalances returns balance data for a set of owner accounts, optionally + /// > filtered by a set of mints /// /// - Parameters: - /// - request: A request containing a single `Ocp_Balance_V1_GetBalanceRequest` message. + /// - request: A request containing a single `Ocp_Balance_V1_GetBalancesRequest` message. /// - options: Options to apply to this RPC. /// - handleResponse: A closure which handles the response, the result of which is /// returned to the caller. Returning from the closure will cancel the RPC if it /// hasn't already finished. /// - Returns: The result of `handleResponse`. - public func getBalance( - request: GRPCCore.ClientRequest, + public func getBalances( + request: GRPCCore.ClientRequest, options: GRPCCore.CallOptions = .defaults, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in try response.message } ) async throws -> Result where Result: Sendable { - try await self.getBalance( + try await self.getBalances( request: request, - serializer: GRPCProtobuf.ProtobufSerializer(), - deserializer: GRPCProtobuf.ProtobufDeserializer(), + serializer: GRPCProtobuf.ProtobufSerializer(), + deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, onResponse: handleResponse ) @@ -166,11 +169,12 @@ extension Ocp_Balance_V1_Balance.ClientProtocol { // Helpers providing sugared APIs for 'ClientProtocol' methods. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension Ocp_Balance_V1_Balance.ClientProtocol { - /// Call the "GetBalance" method. + /// Call the "GetBalances" method. /// /// > Source IDL Documentation: /// > - /// > GetBalance returns balance data for any owner account + /// > GetBalances returns balance data for a set of owner accounts, optionally + /// > filtered by a set of mints /// /// - Parameters: /// - message: request message to send. @@ -180,19 +184,19 @@ extension Ocp_Balance_V1_Balance.ClientProtocol { /// returned to the caller. Returning from the closure will cancel the RPC if it /// hasn't already finished. /// - Returns: The result of `handleResponse`. - public func getBalance( - _ message: Ocp_Balance_V1_GetBalanceRequest, + public func getBalances( + _ message: Ocp_Balance_V1_GetBalancesRequest, metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in try response.message } ) async throws -> Result where Result: Sendable { - let request = GRPCCore.ClientRequest( + let request = GRPCCore.ClientRequest( message: message, metadata: metadata ) - return try await self.getBalance( + return try await self.getBalances( request: request, options: options, onResponse: handleResponse diff --git a/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.pb.swift b/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.pb.swift index 0f46005..4144bd5 100644 --- a/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.pb.swift +++ b/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.pb.swift @@ -20,35 +20,32 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public struct Ocp_Balance_V1_GetBalanceRequest: Sendable { +public struct Ocp_Balance_V1_GetBalancesRequest: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var owner: Ocp_Common_V1_SolanaAccountId { - get {return _owner ?? Ocp_Common_V1_SolanaAccountId()} - set {_owner = newValue} - } - /// Returns true if `owner` has been explicitly set. - public var hasOwner: Bool {return self._owner != nil} - /// Clears the value of `owner`. Subsequent reads from it will return its default value. - public mutating func clearOwner() {self._owner = nil} + /// The owner accounts to fetch balances for + public var owners: [Ocp_Common_V1_SolanaAccountId] = [] + + /// Optional filter to limit the response to balances for the provided mints. + /// When empty, balances for all mints held by each owner are returned. + public var mints: [Ocp_Common_V1_SolanaAccountId] = [] public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} - - fileprivate var _owner: Ocp_Common_V1_SolanaAccountId? = nil } -public struct Ocp_Balance_V1_GetBalanceResponse: Sendable { +public struct Ocp_Balance_V1_GetBalancesResponse: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var result: Ocp_Balance_V1_GetBalanceResponse.Result = .ok + public var result: Ocp_Balance_V1_GetBalancesResponse.Result = .ok - public var coreMintValue: UInt64 = 0 + /// Individual owner balances keyed by owner address + public var balancesByOwner: Dictionary = [:] public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -56,7 +53,6 @@ public struct Ocp_Balance_V1_GetBalanceResponse: Sendable { public typealias RawValue = Int case ok // = 0 case denied // = 1 - case notFound // = 2 case UNRECOGNIZED(Int) public init() { @@ -67,7 +63,6 @@ public struct Ocp_Balance_V1_GetBalanceResponse: Sendable { switch rawValue { case 0: self = .ok case 1: self = .denied - case 2: self = .notFound default: self = .UNRECOGNIZED(rawValue) } } @@ -76,16 +71,14 @@ public struct Ocp_Balance_V1_GetBalanceResponse: Sendable { switch self { case .ok: return 0 case .denied: return 1 - case .notFound: return 2 case .UNRECOGNIZED(let i): return i } } // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Ocp_Balance_V1_GetBalanceResponse.Result] = [ + public static let allCases: [Ocp_Balance_V1_GetBalancesResponse.Result] = [ .ok, .denied, - .notFound, ] } @@ -93,13 +86,140 @@ public struct Ocp_Balance_V1_GetBalanceResponse: Sendable { public init() {} } +public struct Ocp_Balance_V1_OwnerBalance: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// The owner account the balance is for + public var owner: Ocp_Common_V1_SolanaAccountId { + get {return _owner ?? Ocp_Common_V1_SolanaAccountId()} + set {_owner = newValue} + } + /// Returns true if `owner` has been explicitly set. + public var hasOwner: Bool {return self._owner != nil} + /// Clears the value of `owner`. Subsequent reads from it will return its default value. + public mutating func clearOwner() {self._owner = nil} + + /// The total core mint value, in quarks, across all balances for the owner + public var coreMintValue: UInt64 = 0 + + /// Individual balances keyed by mint address + public var balancesByMint: Dictionary = [:] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _owner: Ocp_Common_V1_SolanaAccountId? = nil +} + +public struct Ocp_Balance_V1_MintBalance: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// The mint the balance is for + public var mint: Ocp_Common_V1_SolanaAccountId { + get {return _mint ?? Ocp_Common_V1_SolanaAccountId()} + set {_mint = newValue} + } + /// Returns true if `mint` has been explicitly set. + public var hasMint: Bool {return self._mint != nil} + /// Clears the value of `mint`. Subsequent reads from it will return its default value. + public mutating func clearMint() {self._mint = nil} + + /// The balance value, in quarks, denominated in the core mint + public var coreMintValue: UInt64 = 0 + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _mint: Ocp_Common_V1_SolanaAccountId? = nil +} + // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "ocp.balance.v1" -extension Ocp_Balance_V1_GetBalanceRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".GetBalanceRequest" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}owner\0") +extension Ocp_Balance_V1_GetBalancesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GetBalancesRequest" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}owners\0\u{1}mints\0") + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.owners) }() + case 2: try { try decoder.decodeRepeatedMessageField(value: &self.mints) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.owners.isEmpty { + try visitor.visitRepeatedMessageField(value: self.owners, fieldNumber: 1) + } + if !self.mints.isEmpty { + try visitor.visitRepeatedMessageField(value: self.mints, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Ocp_Balance_V1_GetBalancesRequest, rhs: Ocp_Balance_V1_GetBalancesRequest) -> Bool { + if lhs.owners != rhs.owners {return false} + if lhs.mints != rhs.mints {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Ocp_Balance_V1_GetBalancesResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GetBalancesResponse" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}result\0\u{3}balances_by_owner\0") + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.result) }() + case 2: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMessageMap.self, value: &self.balancesByOwner) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.result != .ok { + try visitor.visitSingularEnumField(value: self.result, fieldNumber: 1) + } + if !self.balancesByOwner.isEmpty { + try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMessageMap.self, value: self.balancesByOwner, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Ocp_Balance_V1_GetBalancesResponse, rhs: Ocp_Balance_V1_GetBalancesResponse) -> Bool { + if lhs.result != rhs.result {return false} + if lhs.balancesByOwner != rhs.balancesByOwner {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Ocp_Balance_V1_GetBalancesResponse.Result: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0OK\0\u{1}DENIED\0") +} + +extension Ocp_Balance_V1_OwnerBalance: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".OwnerBalance" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}owner\0\u{3}core_mint_value\0\u{3}balances_by_mint\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -108,6 +228,8 @@ extension Ocp_Balance_V1_GetBalanceRequest: SwiftProtobuf.Message, SwiftProtobuf // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { case 1: try { try decoder.decodeSingularMessageField(value: &self._owner) }() + case 2: try { try decoder.decodeSingularUInt64Field(value: &self.coreMintValue) }() + case 3: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMessageMap.self, value: &self.balancesByMint) }() default: break } } @@ -121,19 +243,27 @@ extension Ocp_Balance_V1_GetBalanceRequest: SwiftProtobuf.Message, SwiftProtobuf try { if let v = self._owner { try visitor.visitSingularMessageField(value: v, fieldNumber: 1) } }() + if self.coreMintValue != 0 { + try visitor.visitSingularUInt64Field(value: self.coreMintValue, fieldNumber: 2) + } + if !self.balancesByMint.isEmpty { + try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMessageMap.self, value: self.balancesByMint, fieldNumber: 3) + } try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Ocp_Balance_V1_GetBalanceRequest, rhs: Ocp_Balance_V1_GetBalanceRequest) -> Bool { + public static func ==(lhs: Ocp_Balance_V1_OwnerBalance, rhs: Ocp_Balance_V1_OwnerBalance) -> Bool { if lhs._owner != rhs._owner {return false} + if lhs.coreMintValue != rhs.coreMintValue {return false} + if lhs.balancesByMint != rhs.balancesByMint {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension Ocp_Balance_V1_GetBalanceResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".GetBalanceResponse" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}result\0\u{3}core_mint_value\0") +extension Ocp_Balance_V1_MintBalance: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".MintBalance" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}mint\0\u{3}core_mint_value\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -141,7 +271,7 @@ extension Ocp_Balance_V1_GetBalanceResponse: SwiftProtobuf.Message, SwiftProtobu // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 1: try { try decoder.decodeSingularEnumField(value: &self.result) }() + case 1: try { try decoder.decodeSingularMessageField(value: &self._mint) }() case 2: try { try decoder.decodeSingularUInt64Field(value: &self.coreMintValue) }() default: break } @@ -149,23 +279,23 @@ extension Ocp_Balance_V1_GetBalanceResponse: SwiftProtobuf.Message, SwiftProtobu } public func traverse(visitor: inout V) throws { - if self.result != .ok { - try visitor.visitSingularEnumField(value: self.result, fieldNumber: 1) - } + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._mint { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() if self.coreMintValue != 0 { try visitor.visitSingularUInt64Field(value: self.coreMintValue, fieldNumber: 2) } try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Ocp_Balance_V1_GetBalanceResponse, rhs: Ocp_Balance_V1_GetBalanceResponse) -> Bool { - if lhs.result != rhs.result {return false} + public static func ==(lhs: Ocp_Balance_V1_MintBalance, rhs: Ocp_Balance_V1_MintBalance) -> Bool { + if lhs._mint != rhs._mint {return false} if lhs.coreMintValue != rhs.coreMintValue {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } } - -extension Ocp_Balance_V1_GetBalanceResponse.Result: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0OK\0\u{1}DENIED\0\u{1}NOT_FOUND\0") -} diff --git a/ocp.lock b/ocp.lock index a4cda69..cb45f59 100644 --- a/ocp.lock +++ b/ocp.lock @@ -1,4 +1,4 @@ # Pinned upstream contract. Regenerate with scripts/sync-protos.sh . upstream: code-payments/ocp-protobuf-api -commit: ea6418c5561e16771d456062be2fcbd3ddeb9caf -subject: Add the Balance RPC service (#63) +commit: 82202912574e122bba90025fe8b292d5a3f04c05 +subject: Remove GetBalance RPC in favour of GetBalances (#66) diff --git a/proto/balance/v1/ocp_balance_service.proto b/proto/balance/v1/ocp_balance_service.proto index fe217fb..b631857 100644 --- a/proto/balance/v1/ocp_balance_service.proto +++ b/proto/balance/v1/ocp_balance_service.proto @@ -10,21 +10,51 @@ import "common/v1/model.proto"; import "validate/validate.proto"; service Balance { - // GetBalance returns balance data for any owner account - rpc GetBalance(GetBalanceRequest) returns (GetBalanceResponse); + // GetBalances returns balance data for a set of owner accounts, optionally + // filtered by a set of mints + rpc GetBalances(GetBalancesRequest) returns (GetBalancesResponse); } -message GetBalanceRequest { - common.v1.SolanaAccountId owner = 1 [(validate.rules).message.required = true]; +message GetBalancesRequest { + // The owner accounts to fetch balances for + repeated common.v1.SolanaAccountId owners = 1 [(validate.rules).repeated = { + min_items: 1 + max_items: 1024 // Arbitrary + }]; + + // Optional filter to limit the response to balances for the provided mints. + // When empty, balances for all mints held by each owner are returned. + repeated common.v1.SolanaAccountId mints = 2 [(validate.rules).repeated = { + max_items: 1024 // Arbitrary + }]; } -message GetBalanceResponse { +message GetBalancesResponse { Result result = 1; enum Result { OK = 0; DENIED = 1; - NOT_FOUND = 2; } + // Individual owner balances keyed by owner address + map balances_by_owner = 2; +} + +message OwnerBalance { + // The owner account the balance is for + common.v1.SolanaAccountId owner = 1 [(validate.rules).message.required = true]; + + // The total core mint value, in quarks, across all balances for the owner + uint64 core_mint_value = 2; + + // Individual balances keyed by mint address + map balances_by_mint = 3; +} + +message MintBalance { + // The mint the balance is for + common.v1.SolanaAccountId mint = 1 [(validate.rules).message.required = true]; + + // The balance value, in quarks, denominated in the core mint uint64 core_mint_value = 2; }