From c4971e3602aebf6ea1baf0839143a4b3493ef44d Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Mon, 25 May 2026 13:49:36 -0400 Subject: [PATCH] Use confirmed balances for USDC ATA balances --- ocp/balance/calculator.go | 2 +- ocp/data/blockchain.go | 6 +++--- ocp/rpc/transaction/stateless_swap.go | 10 +++------- ocp/worker/geyser/external_deposit.go | 2 +- solana/client.go | 6 +++--- solana/client_with_fallback.go | 6 +++--- solana/client_with_fallback_test.go | 2 +- 7 files changed, 15 insertions(+), 19 deletions(-) diff --git a/ocp/balance/calculator.go b/ocp/balance/calculator.go index 4acded3..606445c 100644 --- a/ocp/balance/calculator.go +++ b/ocp/balance/calculator.go @@ -152,7 +152,7 @@ func CalculateFromBlockchain(ctx context.Context, data ocp_data.Provider, tokenA } // todo: we may need something that's more resistant to RPC nodes with stale account state - quarks, slot, err := data.GetBlockchainBalance(ctx, tokenAccount.PublicKey().ToBase58()) + quarks, slot, err := data.GetBlockchainBalance(ctx, tokenAccount.PublicKey().ToBase58(), solana.CommitmentConfirmed) if err == solana.ErrNoBalance { // We can't tell whether // 1. RPC node is behind, and observed a state before the account existed diff --git a/ocp/data/blockchain.go b/ocp/data/blockchain.go index c114195..522e896 100644 --- a/ocp/data/blockchain.go +++ b/ocp/data/blockchain.go @@ -22,7 +22,7 @@ type BlockchainData interface { GetBlockchainAccountInfo(ctx context.Context, account string, commitment solana.Commitment) (*solana.AccountInfo, uint64, error) GetBlockchainAccountDataAfterBlock(ctx context.Context, account string, slot uint64) ([]byte, uint64, error) - GetBlockchainBalance(ctx context.Context, account string) (uint64, uint64, error) + GetBlockchainBalance(ctx context.Context, account string, commitment solana.Commitment) (uint64, uint64, error) GetBlockchainBlock(ctx context.Context, slot uint64) (*solana.Block, error) GetBlockchainBlockSignatures(ctx context.Context, slot uint64) ([]string, error) GetBlockchainBlocksWithLimit(ctx context.Context, start uint64, limit uint64) ([]uint64, error) @@ -282,7 +282,7 @@ func (dp *BlockchainProvider) GetBlockchainMinimumBalanceForRentExemption(ctx co return res, err } -func (dp *BlockchainProvider) GetBlockchainBalance(ctx context.Context, account string) (uint64, uint64, error) { +func (dp *BlockchainProvider) GetBlockchainBalance(ctx context.Context, account string, commitment solana.Commitment) (uint64, uint64, error) { tracer := metrics.TraceMethodCall(ctx, blockchainProviderMetricsName, "GetBlockchainBalance") defer tracer.End() @@ -291,7 +291,7 @@ func (dp *BlockchainProvider) GetBlockchainBalance(ctx context.Context, account return 0, 0, err } - quarks, slot, err := dp.sc.GetTokenAccountBalance(accountId) + quarks, slot, err := dp.sc.GetTokenAccountBalance(accountId, commitment) if err != nil { tracer.OnError(err) diff --git a/ocp/rpc/transaction/stateless_swap.go b/ocp/rpc/transaction/stateless_swap.go index 0723fc5..c66da39 100644 --- a/ocp/rpc/transaction/stateless_swap.go +++ b/ocp/rpc/transaction/stateless_swap.go @@ -14,6 +14,7 @@ import ( transactionpb "github.com/code-payments/ocp-protobuf-api/generated/go/transaction/v1" "github.com/code-payments/ocp-server/grpc/client" + "github.com/code-payments/ocp-server/ocp/balance" "github.com/code-payments/ocp-server/ocp/common" "github.com/code-payments/ocp-server/ocp/data/swap" "github.com/code-payments/ocp-server/ocp/data/timelock" @@ -159,17 +160,12 @@ func (s *transactionServer) handleStablecoinStatelessSwap( return handleStatelessSwapError(streamer, err) } - ownerFromMintAtaInfo, err := s.data.GetBlockchainTokenAccountInfo( - ctx, - ownerFromMintAta.PublicKey().ToBase58(), - fromMint.PublicKey().ToBase58(), - solana.CommitmentConfirmed, - ) + ownerFromMintAtaBalance, _, err := balance.CalculateFromBlockchain(ctx, s.data, ownerFromMintAta) if err != nil { log.With(zap.Error(err)).Warn("failure getting owner from_mint ata info") return handleStatelessSwapError(streamer, NewSwapValidationError("source ata not found or invalid")) } - if ownerFromMintAtaInfo.Amount < initiateStablecoinSwapReq.SwapAmount { + if ownerFromMintAtaBalance < initiateStablecoinSwapReq.SwapAmount { return handleStatelessSwapError(streamer, NewSwapValidationError("insufficient balance")) } diff --git a/ocp/worker/geyser/external_deposit.go b/ocp/worker/geyser/external_deposit.go index ad14bbf..7df5820 100644 --- a/ocp/worker/geyser/external_deposit.go +++ b/ocp/worker/geyser/external_deposit.go @@ -80,7 +80,7 @@ func maybeInitiateExternalDepositIntoVm(ctx context.Context, data ocp_data.Provi return errors.Wrap(err, "error getting vm deposit ata") } - balance, _, err := data.GetBlockchainBalance(ctx, vmDepositAccounts.Ata.PublicKey().ToBase58()) + balance, _, err := data.GetBlockchainBalance(ctx, vmDepositAccounts.Ata.PublicKey().ToBase58(), solana.CommitmentFinalized) if err == solana.ErrNoBalance { return nil } else if err != nil { diff --git a/solana/client.go b/solana/client.go index 35b0690..8d50492 100644 --- a/solana/client.go +++ b/solana/client.go @@ -189,7 +189,7 @@ type Client interface { GetSignatureStatuses([]Signature) ([]*SignatureStatus, error) GetSignaturesForAddress(owner ed25519.PublicKey, commitment Commitment, limit uint64, before, until string) ([]*TransactionSignature, error) GetSlot(Commitment) (uint64, error) - GetTokenAccountBalance(ed25519.PublicKey) (uint64, uint64, error) + GetTokenAccountBalance(ed25519.PublicKey, Commitment) (uint64, uint64, error) GetTokenAccountsByOwner(owner, mint ed25519.PublicKey) ([]ed25519.PublicKey, error) GetTransaction(Signature, Commitment) (ConfirmedTransaction, error) GetTransactionTokenBalances(Signature) (TransactionTokenBalances, error) @@ -657,14 +657,14 @@ func (c *client) GetBalance(account ed25519.PublicKey) (uint64, error) { return 0, errors.Errorf("invalid value in response") } -func (c *client) GetTokenAccountBalance(account ed25519.PublicKey) (uint64, uint64, error) { +func (c *client) GetTokenAccountBalance(account ed25519.PublicKey, commitment Commitment) (uint64, uint64, error) { var resp struct { Context struct { Slot int64 `json:"slot"` } `json:"context"` Value TokenAmount `json:"value"` } - if err := c.call(&resp, "getTokenAccountBalance", base58.Encode(account[:]), CommitmentFinalized); err != nil { + if err := c.call(&resp, "getTokenAccountBalance", base58.Encode(account[:]), commitment); err != nil { jsonRPCErr, ok := err.(*jsonrpc.RPCError) if !ok { return 0, 0, errors.Wrapf(err, "getTokenAccountBalance() failed to send request") diff --git a/solana/client_with_fallback.go b/solana/client_with_fallback.go index 8bf2abb..3f52f16 100644 --- a/solana/client_with_fallback.go +++ b/solana/client_with_fallback.go @@ -197,10 +197,10 @@ func (c *clientWithFallback) GetSlot(commitment Commitment) (uint64, error) { ) } -func (c *clientWithFallback) GetTokenAccountBalance(account ed25519.PublicKey) (uint64, uint64, error) { +func (c *clientWithFallback) GetTokenAccountBalance(account ed25519.PublicKey, commitment Commitment) (uint64, uint64, error) { return withFallback2( - func() (uint64, uint64, error) { return c.primary.GetTokenAccountBalance(account) }, - func() (uint64, uint64, error) { return c.fallback.GetTokenAccountBalance(account) }, + func() (uint64, uint64, error) { return c.primary.GetTokenAccountBalance(account, commitment) }, + func() (uint64, uint64, error) { return c.fallback.GetTokenAccountBalance(account, commitment) }, ) } diff --git a/solana/client_with_fallback_test.go b/solana/client_with_fallback_test.go index 69bd65d..6774df0 100644 --- a/solana/client_with_fallback_test.go +++ b/solana/client_with_fallback_test.go @@ -115,7 +115,7 @@ func (m *mockClient) GetSignaturesForAddress(ed25519.PublicKey, Commitment, uint m.callCount++ return nil, nil } -func (m *mockClient) GetTokenAccountBalance(ed25519.PublicKey) (uint64, uint64, error) { +func (m *mockClient) GetTokenAccountBalance(ed25519.PublicKey, Commitment) (uint64, uint64, error) { m.callCount++ return 0, 0, nil }