From e1d9088ab21b83c983d17796beb2bf99717f4e1b Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Fri, 23 Jan 2026 14:48:31 -0500 Subject: [PATCH 1/6] Remove GetAllRates RPC from Currency Server --- ocp/rpc/currency/server.go | 42 -------------------------------------- 1 file changed, 42 deletions(-) diff --git a/ocp/rpc/currency/server.go b/ocp/rpc/currency/server.go index 9996106..3c624b8 100644 --- a/ocp/rpc/currency/server.go +++ b/ocp/rpc/currency/server.go @@ -7,7 +7,6 @@ import ( "time" "github.com/google/uuid" - "github.com/pkg/errors" "go.uber.org/zap" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -67,47 +66,6 @@ func NewCurrencyServer( } } -func (s *currencyServer) GetAllRates(ctx context.Context, req *currencypb.GetAllRatesRequest) (resp *currencypb.GetAllRatesResponse, err error) { - log := s.log.With(zap.String("method", "GetAllRates")) - log = client.InjectLoggingMetadata(ctx, log) - - var record *currency.MultiRateRecord - if req.Timestamp != nil && req.Timestamp.AsTime().Before(time.Now().Add(-15*time.Minute)) { - record, err = s.loadExchangeRatesForTime(ctx, req.Timestamp.AsTime()) - } else if req.Timestamp == nil || req.Timestamp.AsTime().Sub(time.Now()) < time.Hour { - record, err = s.loadExchangeRatesLatest(ctx) - } else { - return nil, status.Error(codes.InvalidArgument, "timestamp too far in the future") - } - - if err != nil { - log.With(zap.Error(err)).Warn("failed to load latest rate") - return nil, status.Error(codes.Internal, err.Error()) - } - - protoTime := timestamppb.New(record.Time) - return ¤cypb.GetAllRatesResponse{ - AsOf: protoTime, - Rates: record.Rates, - }, nil -} - -func (s *currencyServer) loadExchangeRatesForTime(ctx context.Context, t time.Time) (*currency.MultiRateRecord, error) { - record, err := s.data.GetAllExchangeRates(ctx, t) - if err != nil { - return nil, errors.Wrap(err, "failed to get price record by date") - } - return record, nil -} - -func (s *currencyServer) loadExchangeRatesLatest(ctx context.Context) (*currency.MultiRateRecord, error) { - latest, err := s.data.GetAllExchangeRates(ctx, currency_util.GetLatestExchangeRateTime()) - if err != nil { - return nil, errors.Wrap(err, "failed to get latest price record") - } - return latest, nil -} - func (s *currencyServer) GetMints(ctx context.Context, req *currencypb.GetMintsRequest) (*currencypb.GetMintsResponse, error) { log := s.log.With(zap.String("method", "GetMints")) log = client.InjectLoggingMetadata(ctx, log) From 19ea2150843c8e00d7c9cccfca422e1257ece5b4 Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Fri, 23 Jan 2026 14:49:16 -0500 Subject: [PATCH 2/6] GetMints RPC now returns latest reserve state for launchpad currencies --- ocp/rpc/currency/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ocp/rpc/currency/server.go b/ocp/rpc/currency/server.go index 3c624b8..ce38fae 100644 --- a/ocp/rpc/currency/server.go +++ b/ocp/rpc/currency/server.go @@ -116,7 +116,7 @@ func (s *currencyServer) GetMints(ctx context.Context, req *currencypb.GetMintsR return nil, status.Error(codes.Internal, "") } - reserveRecord, err := s.data.GetCurrencyReserveAtTime(ctx, mintAccount.PublicKey().ToBase58(), currency_util.GetLatestExchangeRateTime()) + reserveRecord, err := s.data.GetCurrencyReserveAtTime(ctx, mintAccount.PublicKey().ToBase58(), time.Now()) if err != nil { log.With(zap.Error(err)).Warn("failed to load currency reserve record") return nil, status.Error(codes.Internal, "") From 325ebdaffc84ce7b6bc15eb44d23fe01e57600b6 Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Fri, 23 Jan 2026 14:51:59 -0500 Subject: [PATCH 3/6] SubmitIntent only supports VerifiedExchangeData --- ocp/currency/validation.go | 217 -------------------------- ocp/rpc/transaction/intent_handler.go | 42 +---- 2 files changed, 4 insertions(+), 255 deletions(-) diff --git a/ocp/currency/validation.go b/ocp/currency/validation.go index 4a29b5c..070dd93 100644 --- a/ocp/currency/validation.go +++ b/ocp/currency/validation.go @@ -1,7 +1,6 @@ package currency import ( - "context" "crypto/ed25519" "math" "math/big" @@ -9,13 +8,10 @@ import ( currencypb "github.com/code-payments/ocp-protobuf-api/generated/go/currency/v1" transactionpb "github.com/code-payments/ocp-protobuf-api/generated/go/transaction/v1" - "go.uber.org/zap" currency_lib "github.com/code-payments/ocp-server/currency" "github.com/code-payments/ocp-server/ocp/auth" "github.com/code-payments/ocp-server/ocp/common" - ocp_data "github.com/code-payments/ocp-server/ocp/data" - "github.com/code-payments/ocp-server/ocp/data/currency" "github.com/code-payments/ocp-server/solana/currencycreator" ) @@ -236,216 +232,3 @@ func validateLaunchpadCurrencyVerifiedExchangeData(proto *transactionpb.Verified return true, "" } - -// ValidateLegacyClientExchangeData validates legacy proto exchange data provided by a client -func ValidateLegacyClientExchangeData(ctx context.Context, log *zap.Logger, data ocp_data.Provider, proto *transactionpb.ExchangeData) (bool, string, error) { - mint, err := common.GetBackwardsCompatMint(proto.Mint) - if err != nil { - return false, "", err - } - - if common.IsCoreMint(mint) { - return validateCoreMintClientExchangeData(ctx, log, data, proto) - } - return validateCurrencyLaunchpadClientExchangeData(ctx, log, data, proto) -} - -func validateCoreMintClientExchangeData(ctx context.Context, log *zap.Logger, data ocp_data.Provider, proto *transactionpb.ExchangeData) (bool, string, error) { - latestExchangeRateTime := GetLatestExchangeRateTime() - - coreMintQuarksPerUnit := common.GetMintQuarksPerUnit(common.CoreMintAccount) - - clientRate := big.NewFloat(proto.ExchangeRate).SetPrec(defaultPrecision) - clientNativeAmount := big.NewFloat(proto.NativeAmount).SetPrec(defaultPrecision) - clientQuarks := big.NewFloat(float64(proto.Quarks)).SetPrec(defaultPrecision) - - currencyDecimals := currency_lib.GetDecimals(currency_lib.Code(proto.Currency)) - one := big.NewFloat(1.0).SetPrec(defaultPrecision) - minTransferValue := new(big.Float).Quo(one, big.NewFloat(math.Pow10(currencyDecimals))) - - rateErrorThreshold := big.NewFloat(0.001).SetPrec(defaultPrecision) - nativeAmountErrorThreshold := new(big.Float).Quo(minTransferValue, big.NewFloat(2.0)) - - nativeAmountLowerBound := new(big.Float).Sub(clientNativeAmount, nativeAmountErrorThreshold) - if nativeAmountLowerBound.Cmp(nativeAmountErrorThreshold) < 0 { - nativeAmountLowerBound = nativeAmountErrorThreshold - } - nativeAmountUpperBound := new(big.Float).Add(clientNativeAmount, nativeAmountErrorThreshold) - quarksLowerBound := new(big.Float).Mul(new(big.Float).Quo(nativeAmountLowerBound, clientRate), big.NewFloat(float64(coreMintQuarksPerUnit))) - quarksUpperBound := new(big.Float).Mul(new(big.Float).Quo(nativeAmountUpperBound, clientRate), big.NewFloat(float64(coreMintQuarksPerUnit))) - - log = log.With( - zap.String("currency", proto.Currency), - zap.String("client_native_amount", clientNativeAmount.Text('f', 10)), - zap.String("client_exchange_rate", clientRate.Text('f', 10)), - zap.Uint64("client_quarks", proto.Quarks), - zap.String("min_transfer_value", minTransferValue.Text('f', 10)), - zap.String("native_amount_lower_bound", nativeAmountLowerBound.Text('f', 10)), - zap.String("native_amount_upper_bound", nativeAmountUpperBound.Text('f', 10)), - zap.String("quarks_lower_bound", quarksLowerBound.Text('f', 10)), - zap.String("quarks_upper_bound", quarksUpperBound.Text('f', 10)), - ) - - if clientNativeAmount.Cmp(nativeAmountErrorThreshold) < 0 { - log.Info("native amount is less than minimum transfer value error threshold") - return false, "native amount is less than minimum transfer value error threshold", nil - } - - // Find an exchange rate that the client could have fetched from a RPC call - // within a reasonable time in the past - var isClientRateValid bool - for i := range 2 { - exchangeRateTime := latestExchangeRateTime.Add(time.Duration(-i) * timePerExchangeRateUpdate) - - exchangeRateRecord, err := data.GetExchangeRate(ctx, currency_lib.Code(proto.Currency), exchangeRateTime) - if err == currency.ErrNotFound { - continue - } else if err != nil { - return false, "", err - } - actualRate := big.NewFloat(exchangeRateRecord.Rate) - - percentDiff := new(big.Float).Quo(new(big.Float).Abs(new(big.Float).Sub(clientRate, actualRate)), actualRate) - if percentDiff.Cmp(rateErrorThreshold) < 0 { - isClientRateValid = true - break - } - - log.With(zap.String("found_rate", actualRate.Text('f', 10))).Info("exchange rate doesn't match") - } - - if !isClientRateValid { - log.Info("fiat exchange rate is stale or invalid") - return false, "fiat exchange rate is stale or invalid", nil - } - - // Validate that the native amount within half of the minimum transfer value - if clientQuarks.Cmp(quarksLowerBound) < 0 || clientQuarks.Cmp(quarksUpperBound) > 0 { - log.Info("native amount is outside error threshold") - return false, "payment native amount and quark value mismatch", nil - } - - return true, "", nil -} - -func validateCurrencyLaunchpadClientExchangeData(ctx context.Context, log *zap.Logger, data ocp_data.Provider, proto *transactionpb.ExchangeData) (bool, string, error) { - mintAccount, err := common.GetBackwardsCompatMint(proto.Mint) - if err != nil { - return false, "", err - } - - coreMintQuarksPerUnit := common.GetMintQuarksPerUnit(common.CoreMintAccount) - otherMintQuarksPerUnit := common.GetMintQuarksPerUnit(mintAccount) - - clientQuarks := big.NewFloat(float64(proto.Quarks)).SetPrec(defaultPrecision) - clientTokenUnits := new(big.Float).Quo( - clientQuarks, - big.NewFloat(float64(otherMintQuarksPerUnit)).SetPrec(defaultPrecision), - ) - clientRate := big.NewFloat(proto.ExchangeRate).SetPrec(defaultPrecision) - clientNativeAmount := big.NewFloat(proto.NativeAmount).SetPrec(defaultPrecision) - - currencyDecimals := currency_lib.GetDecimals(currency_lib.Code(proto.Currency)) - one := big.NewFloat(1.0).SetPrec(defaultPrecision) - minTransferValue := new(big.Float).Quo(one, big.NewFloat(math.Pow10(currencyDecimals))) - - rateErrorThreshold := big.NewFloat(0.001).SetPrec(defaultPrecision) - nativeAmountErrorThreshold := new(big.Float).Quo(minTransferValue, big.NewFloat(2.0)) - - nativeAmountLowerBound := new(big.Float).Sub(clientNativeAmount, nativeAmountErrorThreshold) - if nativeAmountLowerBound.Cmp(nativeAmountErrorThreshold) < 0 { - nativeAmountLowerBound = nativeAmountErrorThreshold - } - nativeAmountUpperBound := new(big.Float).Add(clientNativeAmount, nativeAmountErrorThreshold) - - log = log.With( - zap.String("currency", proto.Currency), - zap.String("client_native_amount", clientNativeAmount.Text('f', 10)), - zap.String("client_exchange_rate", clientRate.Text('f', 10)), - zap.String("client_token_units", clientTokenUnits.Text('f', 10)), - zap.Uint64("client_quarks", proto.Quarks), - zap.String("min_transfer_value", minTransferValue.Text('f', 10)), - zap.String("native_amount_lower_bound", nativeAmountLowerBound.Text('f', 10)), - zap.String("native_amount_upper_bound", nativeAmountUpperBound.Text('f', 10)), - zap.String("mint", mintAccount.PublicKey().ToBase58()), - ) - - if clientNativeAmount.Cmp(nativeAmountErrorThreshold) < 0 { - log.Info("native amount is less than minimum transfer value error threshold") - return false, "native amount is less than minimum transfer value error threshold", nil - } - - latestExchangeRateTime := GetLatestExchangeRateTime() - for i := range 2 { - exchangeRateTime := latestExchangeRateTime.Add(time.Duration(-i) * timePerExchangeRateUpdate) - - reserveRecord, err := data.GetCurrencyReserveAtTime(ctx, mintAccount.PublicKey().ToBase58(), exchangeRateTime) - if err == currency.ErrNotFound { - continue - } else if err != nil { - return false, "", err - } - - usdExchangeRateRecord, err := data.GetExchangeRate(ctx, currency_lib.USD, exchangeRateTime) - if err == currency.ErrNotFound { - continue - } else if err != nil { - return false, "", err - } - usdRate := big.NewFloat(usdExchangeRateRecord.Rate).SetPrec(defaultPrecision) - - var otherExchangeRateRecord *currency.ExchangeRateRecord - if proto.Currency == string(currency_lib.USD) { - otherExchangeRateRecord = usdExchangeRateRecord - } else { - otherExchangeRateRecord, err = data.GetExchangeRate(ctx, currency_lib.Code(proto.Currency), exchangeRateTime) - if err == currency.ErrNotFound { - continue - } else if err != nil { - return false, "", err - } - } - otherRate := big.NewFloat(otherExchangeRateRecord.Rate).SetPrec(defaultPrecision) - - // How much core mint would be received for a sell against the currency creator program? - coreMintSellValueInQuarks, _ := currencycreator.EstimateSell(¤cycreator.EstimateSellArgs{ - CurrentSupplyInQuarks: reserveRecord.SupplyFromBonding, - SellAmountInQuarks: proto.Quarks, - ValueMintDecimals: uint8(common.CoreMintDecimals), - SellFeeBps: 0, - }) - - // Given the sell value, does it align with the native amount in the target currency - // within half a minimum transfer unit? - coreMintSellValueInUnits := new(big.Float).Quo( - big.NewFloat(float64(coreMintSellValueInQuarks)).SetPrec(defaultPrecision), - big.NewFloat(float64(coreMintQuarksPerUnit)).SetPrec(defaultPrecision), - ) - potentialNativeAmount := new(big.Float).Mul(new(big.Float).Quo(otherRate, usdRate), coreMintSellValueInUnits) - - log := log.With( - zap.String("core_mint_sell_value", coreMintSellValueInUnits.Text('f', 10)), - zap.String("potential_native_amount", potentialNativeAmount.Text('f', 10)), - zap.String("found_usd_rate", usdRate.Text('f', 10)), - zap.String("found_other_rate", otherRate.Text('f', 10)), - ) - - if potentialNativeAmount.Cmp(nativeAmountLowerBound) < 0 || potentialNativeAmount.Cmp(nativeAmountUpperBound) > 0 { - log.Info("native amount is outside error threshold") - continue - } - - // For the valid native amount, is the exchange rate calculated correctly? - expectedRate := new(big.Float).Quo(clientNativeAmount, clientTokenUnits) - percentDiff := new(big.Float).Quo(new(big.Float).Abs(new(big.Float).Sub(clientRate, expectedRate)), expectedRate) - log = log.With(zap.String("potential_exchange_rate", expectedRate.Text('f', 10))) - if percentDiff.Cmp(rateErrorThreshold) > 0 { - log.Info("exchange rate is outside error threshold") - continue - } - - return true, "", nil - } - - return false, "fiat exchange data is stale or invalid", nil -} diff --git a/ocp/rpc/transaction/intent_handler.go b/ocp/rpc/transaction/intent_handler.go index b651e80..5bab649 100644 --- a/ocp/rpc/transaction/intent_handler.go +++ b/ocp/rpc/transaction/intent_handler.go @@ -410,11 +410,6 @@ func (h *SendPublicPaymentIntentHandler) PopulateMetadata(ctx context.Context, i exchangeRate = currency_util.CalculateExchangeRate(mint, typed.ClientExchangeData.NativeAmount, typed.ClientExchangeData.Quarks) } quarks = typed.ClientExchangeData.Quarks - case *transactionpb.SendPublicPaymentMetadata_ServerExchangeData: // todo: deprecate this flow - currencyCode = currency_lib.Code(typed.ServerExchangeData.Currency) - nativeAmount = typed.ServerExchangeData.NativeAmount - exchangeRate = typed.ServerExchangeData.ExchangeRate - quarks = typed.ServerExchangeData.Quarks default: return NewIntentDeniedError("client exchange data not provided") } @@ -608,10 +603,8 @@ func (h *SendPublicPaymentIntentHandler) AllowCreation(ctx context.Context, inte if err := validateExchangeDataWithinIntent(typedMetadata.Mint, typed.ClientExchangeData); err != nil { return err } - case *transactionpb.SendPublicPaymentMetadata_ServerExchangeData: - if err := validateLegacyExchangeDataWithinIntent(ctx, h.log, h.data, typedMetadata.Mint, typed.ServerExchangeData); err != nil { - return err - } + default: + return NewIntentDeniedError("client exchange data not provided") } // @@ -677,8 +670,8 @@ func (h *SendPublicPaymentIntentHandler) validateActions( switch typed := metadata.ExchangeData.(type) { case *transactionpb.SendPublicPaymentMetadata_ClientExchangeData: quarks = typed.ClientExchangeData.Quarks - case *transactionpb.SendPublicPaymentMetadata_ServerExchangeData: - quarks = typed.ServerExchangeData.Quarks + default: + return NewIntentDeniedError("client exchange data not provided") } // @@ -1836,33 +1829,6 @@ func validateExchangeDataWithinIntent(intentMint *commonpb.SolanaAccountId, prot return nil } -func validateLegacyExchangeDataWithinIntent(ctx context.Context, log *zap.Logger, data ocp_data.Provider, intentMint *commonpb.SolanaAccountId, proto *transactionpb.ExchangeData) error { - intentMintAccount, err := common.GetBackwardsCompatMint(intentMint) - if err != nil { - return err - } - - exchangeMintAccount, err := common.GetBackwardsCompatMint(proto.Mint) - if err != nil { - return err - } - - if !bytes.Equal(intentMintAccount.PublicKey().ToBytes(), exchangeMintAccount.PublicKey().ToBytes()) { - return NewIntentValidationErrorf("expected exchange data mint to be %s", intentMintAccount.PublicKey().ToBase58()) - } - - isValid, message, err := currency_util.ValidateLegacyClientExchangeData(ctx, log, data, proto) - if err != nil { - return err - } else if !isValid { - if strings.Contains(message, "stale") { - return NewStaleStateError(message) - } - return NewIntentValidationError(message) - } - return nil -} - func validateFeePayments( ctx context.Context, conf *conf, From 130201df44be6d51833ee8e3b0fe497653f53108 Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Mon, 26 Jan 2026 00:40:00 -0500 Subject: [PATCH 4/6] Remove GetLatestExchangeRateTime --- ocp/currency/time.go | 26 -------------------------- ocp/rpc/messaging/testutil.go | 3 +-- ocp/rpc/transaction/airdrop.go | 2 +- ocp/rpc/transaction/limits.go | 3 ++- 4 files changed, 4 insertions(+), 30 deletions(-) delete mode 100644 ocp/currency/time.go diff --git a/ocp/currency/time.go b/ocp/currency/time.go deleted file mode 100644 index b209889..0000000 --- a/ocp/currency/time.go +++ /dev/null @@ -1,26 +0,0 @@ -package currency - -import ( - "time" -) - -const ( - exchangeRateUpdatesPerHour = 4 - timePerExchangeRateUpdate = time.Hour / exchangeRateUpdatesPerHour -) - -// GetLatestExchangeRateTime gets the latest time for fetching an exchange rate. -// By synchronizing on a time, we can eliminate the amount of perceived volatility -// over short time spans. -// -// Deprecated: Use real-time data and favour volatility -func GetLatestExchangeRateTime() time.Time { - // Standardize to concrete 15 minute intervals to reduce perceived volatility. - // Notably, don't fall exactly on the 15 minute interval, so we remove 1 second. - // The way our postgres DB query is setup, the start of UTC day is unlikely to - // generate results. - secondsInUpdateInterval := int64(timePerExchangeRateUpdate / time.Second) - queryTimeUnix := time.Now().Unix() - queryTimeUnix = queryTimeUnix - (queryTimeUnix % secondsInUpdateInterval) - 1 - return time.Unix(queryTimeUnix, 0) -} diff --git a/ocp/rpc/messaging/testutil.go b/ocp/rpc/messaging/testutil.go index 84c126a..405e7b5 100644 --- a/ocp/rpc/messaging/testutil.go +++ b/ocp/rpc/messaging/testutil.go @@ -22,7 +22,6 @@ import ( "github.com/code-payments/ocp-server/ocp/auth" "github.com/code-payments/ocp-server/ocp/common" - currency_util "github.com/code-payments/ocp-server/ocp/currency" ocp_data "github.com/code-payments/ocp-server/ocp/data" "github.com/code-payments/ocp-server/ocp/data/account" "github.com/code-payments/ocp-server/ocp/data/currency" @@ -70,7 +69,7 @@ func setup(t *testing.T, enableMultiServer bool) (env testEnv, cleanup func()) { subsidizer := testutil.SetupRandomSubsidizer(t, data) require.NoError(t, data.ImportExchangeRates(context.Background(), ¤cy.MultiRateRecord{ - Time: currency_util.GetLatestExchangeRateTime(), + Time: time.Now(), Rates: map[string]float64{ "usd": 0.1, }, diff --git a/ocp/rpc/transaction/airdrop.go b/ocp/rpc/transaction/airdrop.go index d9c9224..e84b6da 100644 --- a/ocp/rpc/transaction/airdrop.go +++ b/ocp/rpc/transaction/airdrop.go @@ -205,7 +205,7 @@ func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner additionalQuarks = 1 } - exchangeRateRecord, err := s.data.GetExchangeRate(ctx, currencyCode, currency_util.GetLatestExchangeRateTime()) + exchangeRateRecord, err := s.data.GetExchangeRate(ctx, currencyCode, time.Now()) if err != nil { log.With(zap.Error(err)).Warn("failure getting other rate") return nil, err diff --git a/ocp/rpc/transaction/limits.go b/ocp/rpc/transaction/limits.go index e1bc64f..71a1fb3 100644 --- a/ocp/rpc/transaction/limits.go +++ b/ocp/rpc/transaction/limits.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math" + "time" "go.uber.org/zap" "google.golang.org/grpc/codes" @@ -34,7 +35,7 @@ func (s *transactionServer) GetLimits(ctx context.Context, req *transactionpb.Ge return nil, err } - multiRateRecord, err := s.data.GetAllExchangeRates(ctx, currency_util.GetLatestExchangeRateTime()) + multiRateRecord, err := s.data.GetAllExchangeRates(ctx, time.Now()) if err != nil { log.With(zap.Error(err)).Warn("failure getting current exchange rates") return nil, status.Error(codes.Internal, "") From df6672c1ea2c5a39911e344fa81e65cff577b2ff Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Mon, 26 Jan 2026 21:24:02 -0500 Subject: [PATCH 5/6] GetMints returns live mint data --- ocp/rpc/currency/server.go | 12 +++++++++--- ocp/rpc/currency/worker.go | 21 ++++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/ocp/rpc/currency/server.go b/ocp/rpc/currency/server.go index b00ebe1..7c77afe 100644 --- a/ocp/rpc/currency/server.go +++ b/ocp/rpc/currency/server.go @@ -117,9 +117,15 @@ func (s *currencyServer) GetMints(ctx context.Context, req *currencypb.GetMintsR return nil, status.Error(codes.Internal, "") } - reserveRecord, err := s.data.GetCurrencyReserveAtTime(ctx, mintAccount.PublicKey().ToBase58(), time.Now()) + err = s.liveMintStateWorker.waitForData(ctx) if err != nil { - log.With(zap.Error(err)).Warn("failed to load currency reserve record") + log.With(zap.Error(err)).Warn("failed to wait for live mint data") + return nil, status.Error(codes.Internal, "") + } + + liveReserveState, err := s.liveMintStateWorker.getReserveState(mintAccount) + if err != nil { + log.With(zap.Error(err)).Warn("failed to get live mint reserve state") return nil, status.Error(codes.Internal, "") } @@ -180,7 +186,7 @@ func (s *currencyServer) GetMints(ctx context.Context, req *currencypb.GetMintsR Authority: currencyAuthorityAccount.ToProto(), MintVault: mintVaultAccount.ToProto(), CoreMintVault: coreMintVaultAccount.ToProto(), - SupplyFromBonding: reserveRecord.SupplyFromBonding, + SupplyFromBonding: liveReserveState.SupplyFromBonding, SellFeeBps: uint32(metadataRecord.SellFeeBps), }, CreatedAt: timestamppb.New(metadataRecord.CreatedAt), diff --git a/ocp/rpc/currency/worker.go b/ocp/rpc/currency/worker.go index 23e1079..44c8b96 100644 --- a/ocp/rpc/currency/worker.go +++ b/ocp/rpc/currency/worker.go @@ -1,6 +1,7 @@ package currency import ( + "bytes" "context" "crypto/ed25519" "sync" @@ -11,6 +12,7 @@ import ( commonpb "github.com/code-payments/ocp-protobuf-api/generated/go/common/v1" currencypb "github.com/code-payments/ocp-protobuf-api/generated/go/currency/v1" + "github.com/pkg/errors" "github.com/code-payments/ocp-server/ocp/auth" "github.com/code-payments/ocp-server/ocp/common" @@ -129,7 +131,7 @@ func (m *liveMintStateWorker) unregisterStream(id string) { } } -// WaitForData blocks until initial data is loaded or context is cancelled +// waitForData blocks until initial data is loaded or context is cancelled func (m *liveMintStateWorker) waitForData(ctx context.Context) error { select { case <-m.dataReady: @@ -139,7 +141,7 @@ func (m *liveMintStateWorker) waitForData(ctx context.Context) error { } } -// GetExchangeRates returns the current pre-signed exchange rate data +// getExchangeRates returns the current pre-signed exchange rate data func (m *liveMintStateWorker) getExchangeRates() *liveExchangeRateData { m.stateMu.RLock() defer m.stateMu.RUnlock() @@ -147,7 +149,7 @@ func (m *liveMintStateWorker) getExchangeRates() *liveExchangeRateData { return m.exchangeRates } -// GetReserveStates returns all current pre-signed launchpad currency reserve states +// getReserveStates returns all current pre-signed launchpad currency reserve states func (m *liveMintStateWorker) getReserveStates() []*liveReserveStateData { m.stateMu.RLock() defer m.stateMu.RUnlock() @@ -159,6 +161,19 @@ func (m *liveMintStateWorker) getReserveStates() []*liveReserveStateData { return result } +// getReserveState returns a current pre-signed launchpad currency reserve state for a mint +func (m *liveMintStateWorker) getReserveState(mint *common.Account) (*liveReserveStateData, error) { + m.stateMu.RLock() + defer m.stateMu.RUnlock() + + for _, data := range m.launchpadReserves { + if bytes.Equal(mint.PublicKey().ToBytes(), data.Mint.PublicKey().ToBytes()) { + return data, nil + } + } + return nil, errors.New("not found") +} + func (m *liveMintStateWorker) tryMarkDataReady() { m.initMu.Lock() defer m.initMu.Unlock() From 4a18b4d537ee445066e62631687732ed07db9372 Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Fri, 30 Jan 2026 15:44:29 -0500 Subject: [PATCH 6/6] Pull ocp-protobuf-api v0.12.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7a902b0..8712355 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( filippo.io/edwards25519 v1.1.0 github.com/aws/aws-sdk-go-v2 v0.17.0 github.com/code-payments/code-vm-indexer v1.2.0 - github.com/code-payments/ocp-protobuf-api v0.10.0 + github.com/code-payments/ocp-protobuf-api v0.12.0 github.com/emirpasic/gods v1.12.0 github.com/envoyproxy/protoc-gen-validate v1.2.1 github.com/golang/protobuf v1.5.4 diff --git a/go.sum b/go.sum index 3834e1b..920617c 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/code-payments/code-vm-indexer v1.2.0 h1:rSHpBMiT9BKgmKcXg/VIoi/h0t7jNxGx07Qz59m+6Q0= github.com/code-payments/code-vm-indexer v1.2.0/go.mod h1:vn91YN2qNqb+gGJeZe2+l+TNxVmEEiRHXXnIn2Y40h8= -github.com/code-payments/ocp-protobuf-api v0.10.0 h1:l9Yh3eXdhvgBQS/evg1HYMERXlr7ymASHekAhW/RUmA= -github.com/code-payments/ocp-protobuf-api v0.10.0/go.mod h1:tw6BooY5a8l6CtSZnKOruyKII0W04n89pcM4BizrgG8= +github.com/code-payments/ocp-protobuf-api v0.12.0 h1:X5lo7ABDYxsiC/D3viFuNJUhTWKISZBg6MzAy2PP10Y= +github.com/code-payments/ocp-protobuf-api v0.12.0/go.mod h1:tw6BooY5a8l6CtSZnKOruyKII0W04n89pcM4BizrgG8= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 h1:NmTXa/uVnDyp0TY5MKi197+3HWcnYWfnHGyaFthlnGw= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=