diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e3c6c..337d0f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,34 @@ 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.6.0 + +Synced to [`ocp-protobuf-api@e3d25a85`](https://github.com/code-payments/ocp-protobuf-api/commit/e3d25a85b1e1ce088e956714e1a71248322e215f), +picking up [#67](https://github.com/code-payments/ocp-protobuf-api/pull/67) (docs only) and +[#68](https://github.com/code-payments/ocp-protobuf-api/pull/68). Only `balance.v1` moved. + +`GetBalances` can now report fiat values alongside core-mint values. The change is opt-in per +request, so the upgrade is free: a caller that sends no currency codes gets exactly what it got +before. + +### Added + +- `GetBalancesRequest.currency_codes` (field 3), a `repeated string` of lowercase ISO 4217 codes + such as `"usd"`. At most 256, no duplicates, each matching `^[a-z]{3,4}$`. Empty means core-mint + values only. +- `OwnerBalance.fiat_values_by_currency` (field 4), a `map` holding the owner's + total across all balances, keyed by currency code. +- `MintBalance.fiat_values_by_currency` (field 3), the same map for a single mint's balance. + +Both maps are populated only for the codes the request asked for. A code the caller did not request +is absent, not zero. + +### Unchanged + +Nothing was renumbered, and nothing existing changed type or meaning. The new fields take the next +free number in each message. No service, RPC, message or enum case was removed. Everything else in +the diff is comments. + ## 0.5.0 No contract change. Still synced to diff --git a/Sources/OCPClientProtocol/ContractInfo.swift b/Sources/OCPClientProtocol/ContractInfo.swift index 2c9023b..1dfcdfe 100644 --- a/Sources/OCPClientProtocol/ContractInfo.swift +++ b/Sources/OCPClientProtocol/ContractInfo.swift @@ -2,7 +2,7 @@ public enum OCPContractInfo { public static let version = "0.5.0-dev" - public static let protoCommit = "82202912574e122bba90025fe8b292d5a3f04c05" + public static let protoCommit = "e3d25a85b1e1ce088e956714e1a71248322e215f" public static var isLocal: Bool { protoCommit == localSentinel } diff --git a/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.grpc.swift b/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.grpc.swift index a337431..6d40a15 100644 --- a/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.grpc.swift +++ b/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.grpc.swift @@ -59,7 +59,8 @@ extension Ocp_Balance_V1_Balance { /// > Source IDL Documentation: /// > /// > GetBalances returns balance data for a set of owner accounts, optionally - /// > filtered by a set of mints + /// > filtered by a set of mints. Balances are always denominated in the core + /// > mint, and can additionally be denominated in a set of fiat currencies. /// /// - Parameters: /// - request: A request containing a single `Ocp_Balance_V1_GetBalancesRequest` message. @@ -100,7 +101,8 @@ extension Ocp_Balance_V1_Balance { /// > Source IDL Documentation: /// > /// > GetBalances returns balance data for a set of owner accounts, optionally - /// > filtered by a set of mints + /// > filtered by a set of mints. Balances are always denominated in the core + /// > mint, and can additionally be denominated in a set of fiat currencies. /// /// - Parameters: /// - request: A request containing a single `Ocp_Balance_V1_GetBalancesRequest` message. @@ -140,7 +142,8 @@ extension Ocp_Balance_V1_Balance.ClientProtocol { /// > Source IDL Documentation: /// > /// > GetBalances returns balance data for a set of owner accounts, optionally - /// > filtered by a set of mints + /// > filtered by a set of mints. Balances are always denominated in the core + /// > mint, and can additionally be denominated in a set of fiat currencies. /// /// - Parameters: /// - request: A request containing a single `Ocp_Balance_V1_GetBalancesRequest` message. @@ -174,7 +177,8 @@ extension Ocp_Balance_V1_Balance.ClientProtocol { /// > Source IDL Documentation: /// > /// > GetBalances returns balance data for a set of owner accounts, optionally - /// > filtered by a set of mints + /// > filtered by a set of mints. Balances are always denominated in the core + /// > mint, and can additionally be denominated in a set of fiat currencies. /// /// - Parameters: /// - message: request message to send. diff --git a/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.pb.swift b/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.pb.swift index 4144bd5..343a8e5 100644 --- a/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.pb.swift +++ b/Sources/OCPClientProtocol/balance_v1_ocp_balance_service.pb.swift @@ -32,6 +32,11 @@ public struct Ocp_Balance_V1_GetBalancesRequest: Sendable { /// When empty, balances for all mints held by each owner are returned. public var mints: [Ocp_Common_V1_SolanaAccountId] = [] + /// Optional set of ISO 4217 alpha-3 currency codes (e.g., "usd") to also + /// denominate balance values in, in addition to the core mint value. When + /// empty, only core mint values are returned. + public var currencyCodes: [String] = [] + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -107,6 +112,11 @@ public struct Ocp_Balance_V1_OwnerBalance: Sendable { /// Individual balances keyed by mint address public var balancesByMint: Dictionary = [:] + /// The total fiat value across all balances for the owner, keyed by currency + /// code. Only populated for the currency codes requested in + /// GetBalancesRequest.currency_codes. + public var fiatValuesByCurrency: Dictionary = [:] + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -132,6 +142,11 @@ public struct Ocp_Balance_V1_MintBalance: Sendable { /// The balance value, in quarks, denominated in the core mint public var coreMintValue: UInt64 = 0 + /// The balance value denominated in fiat, keyed by currency code. Only + /// populated for the currency codes requested in + /// GetBalancesRequest.currency_codes. + public var fiatValuesByCurrency: Dictionary = [:] + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -145,7 +160,7 @@ fileprivate let _protobuf_package = "ocp.balance.v1" 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 static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}owners\0\u{1}mints\0\u{3}currency_codes\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -155,6 +170,7 @@ extension Ocp_Balance_V1_GetBalancesRequest: SwiftProtobuf.Message, SwiftProtobu switch fieldNumber { case 1: try { try decoder.decodeRepeatedMessageField(value: &self.owners) }() case 2: try { try decoder.decodeRepeatedMessageField(value: &self.mints) }() + case 3: try { try decoder.decodeRepeatedStringField(value: &self.currencyCodes) }() default: break } } @@ -167,12 +183,16 @@ extension Ocp_Balance_V1_GetBalancesRequest: SwiftProtobuf.Message, SwiftProtobu if !self.mints.isEmpty { try visitor.visitRepeatedMessageField(value: self.mints, fieldNumber: 2) } + if !self.currencyCodes.isEmpty { + try visitor.visitRepeatedStringField(value: self.currencyCodes, fieldNumber: 3) + } 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.currencyCodes != rhs.currencyCodes {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -219,7 +239,7 @@ extension Ocp_Balance_V1_GetBalancesResponse.Result: SwiftProtobuf._ProtoNamePro 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 static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}owner\0\u{3}core_mint_value\0\u{3}balances_by_mint\0\u{3}fiat_values_by_currency\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -230,6 +250,7 @@ extension Ocp_Balance_V1_OwnerBalance: SwiftProtobuf.Message, SwiftProtobuf._Mes 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) }() + case 4: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap.self, value: &self.fiatValuesByCurrency) }() default: break } } @@ -249,6 +270,9 @@ extension Ocp_Balance_V1_OwnerBalance: SwiftProtobuf.Message, SwiftProtobuf._Mes if !self.balancesByMint.isEmpty { try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMessageMap.self, value: self.balancesByMint, fieldNumber: 3) } + if !self.fiatValuesByCurrency.isEmpty { + try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap.self, value: self.fiatValuesByCurrency, fieldNumber: 4) + } try unknownFields.traverse(visitor: &visitor) } @@ -256,6 +280,7 @@ extension Ocp_Balance_V1_OwnerBalance: SwiftProtobuf.Message, SwiftProtobuf._Mes if lhs._owner != rhs._owner {return false} if lhs.coreMintValue != rhs.coreMintValue {return false} if lhs.balancesByMint != rhs.balancesByMint {return false} + if lhs.fiatValuesByCurrency != rhs.fiatValuesByCurrency {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -263,7 +288,7 @@ extension Ocp_Balance_V1_OwnerBalance: SwiftProtobuf.Message, SwiftProtobuf._Mes 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 static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}mint\0\u{3}core_mint_value\0\u{3}fiat_values_by_currency\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -273,6 +298,7 @@ extension Ocp_Balance_V1_MintBalance: SwiftProtobuf.Message, SwiftProtobuf._Mess switch fieldNumber { case 1: try { try decoder.decodeSingularMessageField(value: &self._mint) }() case 2: try { try decoder.decodeSingularUInt64Field(value: &self.coreMintValue) }() + case 3: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap.self, value: &self.fiatValuesByCurrency) }() default: break } } @@ -289,12 +315,16 @@ extension Ocp_Balance_V1_MintBalance: SwiftProtobuf.Message, SwiftProtobuf._Mess if self.coreMintValue != 0 { try visitor.visitSingularUInt64Field(value: self.coreMintValue, fieldNumber: 2) } + if !self.fiatValuesByCurrency.isEmpty { + try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap.self, value: self.fiatValuesByCurrency, fieldNumber: 3) + } try unknownFields.traverse(visitor: &visitor) } 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.fiatValuesByCurrency != rhs.fiatValuesByCurrency {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/ocp.lock b/ocp.lock index cb45f59..0e51570 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: 82202912574e122bba90025fe8b292d5a3f04c05 -subject: Remove GetBalance RPC in favour of GetBalances (#66) +commit: e3d25a85b1e1ce088e956714e1a71248322e215f +subject: GetBalance now supports balance values across many fiat currencies (#68) diff --git a/proto/balance/v1/ocp_balance_service.proto b/proto/balance/v1/ocp_balance_service.proto index b631857..40f7f3a 100644 --- a/proto/balance/v1/ocp_balance_service.proto +++ b/proto/balance/v1/ocp_balance_service.proto @@ -11,7 +11,8 @@ import "validate/validate.proto"; service Balance { // GetBalances returns balance data for a set of owner accounts, optionally - // filtered by a set of mints + // filtered by a set of mints. Balances are always denominated in the core + // mint, and can additionally be denominated in a set of fiat currencies. rpc GetBalances(GetBalancesRequest) returns (GetBalancesResponse); } @@ -27,6 +28,19 @@ message GetBalancesRequest { repeated common.v1.SolanaAccountId mints = 2 [(validate.rules).repeated = { max_items: 1024 // Arbitrary }]; + + // Optional set of ISO 4217 alpha-3 currency codes (e.g., "usd") to also + // denominate balance values in, in addition to the core mint value. When + // empty, only core mint values are returned. + repeated string currency_codes = 3 [(validate.rules).repeated = { + max_items: 256 + unique: true + items: { + string: { + pattern: "^[a-z]{3,4}$" + } + } + }]; } message GetBalancesResponse { @@ -49,6 +63,11 @@ message OwnerBalance { // Individual balances keyed by mint address map balances_by_mint = 3; + + // The total fiat value across all balances for the owner, keyed by currency + // code. Only populated for the currency codes requested in + // GetBalancesRequest.currency_codes. + map fiat_values_by_currency = 4; } message MintBalance { @@ -57,4 +76,9 @@ message MintBalance { // The balance value, in quarks, denominated in the core mint uint64 core_mint_value = 2; + + // The balance value denominated in fiat, keyed by currency code. Only + // populated for the currency codes requested in + // GetBalancesRequest.currency_codes. + map fiat_values_by_currency = 3; }