From ee012df0b36b21d5c8519d967ab519d31cac0c24 Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Mon, 26 Jan 2026 14:56:39 -0500 Subject: [PATCH 1/2] Add GetUsdCostBasis to intent data store --- ocp/data/intent/memory/store.go | 41 ++++++++++++++++++++++++ ocp/data/intent/postgres/model.go | 39 +++++++++++++++++++++++ ocp/data/intent/postgres/store.go | 5 +++ ocp/data/intent/store.go | 5 +++ ocp/data/intent/tests/tests.go | 53 +++++++++++++++++++++++++++++++ ocp/data/internal.go | 4 +++ 6 files changed, 147 insertions(+) diff --git a/ocp/data/intent/memory/store.go b/ocp/data/intent/memory/store.go index 6ac11d0..fc972ca 100644 --- a/ocp/data/intent/memory/store.go +++ b/ocp/data/intent/memory/store.go @@ -364,3 +364,44 @@ func (s *store) GetTransactedAmountForAntiMoneyLaundering(ctx context.Context, o items = s.filterByWithdrawalFlag(items, false) return sumQuarkAmount(items), sumUsdMarketValue(items), nil } + +func (s *store) GetUsdCostBasis(ctx context.Context, owner string, mint string) (float64, error) { + s.mu.Lock() + defer s.mu.Unlock() + + var costBasis float64 + + for _, item := range s.records { + if item.MintAccount != mint { + continue + } + + if item.State == intent.StateRevoked { + continue + } + + switch item.IntentType { + case intent.ExternalDeposit: + // Owner is destination, add USD + if item.InitiatorOwnerAccount == owner { + costBasis += item.ExternalDepositMetadata.UsdMarketValue + } + case intent.ReceivePaymentsPublicly: + // Owner is destination, add USD + if item.InitiatorOwnerAccount == owner { + costBasis += item.ReceivePaymentsPubliclyMetadata.UsdMarketValue + } + case intent.SendPublicPayment: + // Owner as initiator is source, subtract USD + if item.InitiatorOwnerAccount == owner { + costBasis -= item.SendPublicPaymentMetadata.UsdMarketValue + } + // Owner as destination_owner is destination, add USD + if item.SendPublicPaymentMetadata.DestinationOwnerAccount == owner { + costBasis += item.SendPublicPaymentMetadata.UsdMarketValue + } + } + } + + return costBasis, nil +} diff --git a/ocp/data/intent/postgres/model.go b/ocp/data/intent/postgres/model.go index 43b0f38..d261607 100644 --- a/ocp/data/intent/postgres/model.go +++ b/ocp/data/intent/postgres/model.go @@ -574,3 +574,42 @@ func dbGetTransactedAmountForAntiMoneyLaundering(ctx context.Context, db *sqlx.D } return uint64(res.TotalQuarkValue.Int64), res.TotalUsdMarketValue.Float64, nil } + +func dbGetUsdCostBasis(ctx context.Context, db *sqlx.DB, owner string, mint string) (float64, error) { + var res sql.NullFloat64 + + // For backwards compatibility, the mint column is NULL for core mint + var mintFilter string + var params []any + if mint == config.CoreMintPublicKeyString { + mintFilter = "mint IS NULL" + params = []any{owner, intent.StateRevoked, intent.ExternalDeposit, intent.ReceivePaymentsPublicly, intent.SendPublicPayment} + } else { + mintFilter = "mint = $6" + params = []any{owner, intent.StateRevoked, intent.ExternalDeposit, intent.ReceivePaymentsPublicly, intent.SendPublicPayment, mint} + } + + // USD received as destination: + // - ExternalDeposit, ReceivePaymentsPublicly where owner is initiator + // - SendPublicPayment where owner is destination_owner + // USD sent as source: + // - SendPublicPayment where owner is initiator + query := fmt.Sprintf(`SELECT + (SELECT COALESCE(SUM(usd_market_value), 0) FROM %s WHERE owner = $1 AND %s AND state != $2 AND intent_type IN ($3, $4)) + + (SELECT COALESCE(SUM(usd_market_value), 0) FROM %s WHERE destination_owner = $1 AND %s AND state != $2 AND intent_type = $5) - + (SELECT COALESCE(SUM(usd_market_value), 0) FROM %s WHERE owner = $1 AND %s AND state != $2 AND intent_type = $5);`, + intentTableName, mintFilter, + intentTableName, mintFilter, + intentTableName, mintFilter, + ) + + err := db.GetContext(ctx, &res, query, params...) + if err != nil { + return 0, err + } + + if !res.Valid { + return 0, nil + } + return res.Float64, nil +} diff --git a/ocp/data/intent/postgres/store.go b/ocp/data/intent/postgres/store.go index 0bce4e2..9ae6ee9 100644 --- a/ocp/data/intent/postgres/store.go +++ b/ocp/data/intent/postgres/store.go @@ -94,3 +94,8 @@ func (s *store) GetGiftCardClaimedIntent(ctx context.Context, giftCardVault stri func (s *store) GetTransactedAmountForAntiMoneyLaundering(ctx context.Context, owner string, since time.Time) (uint64, float64, error) { return dbGetTransactedAmountForAntiMoneyLaundering(ctx, s.db, owner, since) } + +// GetUsdCostBasis gets the net USD market value for an owner account and mint. +func (s *store) GetUsdCostBasis(ctx context.Context, owner string, mint string) (float64, error) { + return dbGetUsdCostBasis(ctx, s.db, owner, mint) +} diff --git a/ocp/data/intent/store.go b/ocp/data/intent/store.go index b852a4c..7e58f7d 100644 --- a/ocp/data/intent/store.go +++ b/ocp/data/intent/store.go @@ -39,4 +39,9 @@ type Store interface { // GetTransactedAmountForAntiMoneyLaundering gets the total transacted core mint quarks and the // corresponding USD market value for an owner since a timestamp. GetTransactedAmountForAntiMoneyLaundering(ctx context.Context, owner string, since time.Time) (uint64, float64, error) + + // GetUsdCostBasis gets the net USD market value for an owner account and mint. + // The USD market value is subtracted if the owner is the source, and it is added + // if the owner is the destination. + GetUsdCostBasis(ctx context.Context, owner string, mint string) (float64, error) } diff --git a/ocp/data/intent/tests/tests.go b/ocp/data/intent/tests/tests.go index d0c6701..b5109c7 100644 --- a/ocp/data/intent/tests/tests.go +++ b/ocp/data/intent/tests/tests.go @@ -26,6 +26,7 @@ func RunTests(t *testing.T, s intent.Store, teardown func()) { testGetGiftCardClaimedIntent, testGetTransactedAmountForAntiMoneyLaundering, testGetByOwner, + testGetUsdCostBasis, } { tf(t, s) teardown() @@ -609,3 +610,55 @@ func testGetByOwner(t *testing.T, s intent.Store) { } }) } + +func testGetUsdCostBasis(t *testing.T, s intent.Store) { + t.Run("testGetUsdCostBasis", func(t *testing.T) { + ctx := context.Background() + + // No intents results in zero cost basis + costBasis, err := s.GetUsdCostBasis(ctx, "o1", "mint") + require.NoError(t, err) + assert.EqualValues(t, 0, costBasis) + + records := []*intent.Record{ + // ExternalDeposit: o1 receives +100 USD + {IntentId: "t1", IntentType: intent.ExternalDeposit, InitiatorOwnerAccount: "o1", ExternalDepositMetadata: &intent.ExternalDepositMetadata{DestinationTokenAccount: "a1", Quantity: 1000, ExchangeCurrency: currency.USD, ExchangeRate: 0.1, NativeAmount: 100, UsdMarketValue: 100}, MintAccount: "mint", State: intent.StateConfirmed}, + // ReceivePaymentsPublicly: o1 receives +50 USD + {IntentId: "t2", IntentType: intent.ReceivePaymentsPublicly, InitiatorOwnerAccount: "o1", ReceivePaymentsPubliclyMetadata: &intent.ReceivePaymentsPubliclyMetadata{Source: "s1", Quantity: 500, OriginalExchangeCurrency: currency.USD, OriginalExchangeRate: 0.1, OriginalNativeAmount: 50, UsdMarketValue: 50}, MintAccount: "mint", State: intent.StateConfirmed}, + // SendPublicPayment: o1 sends -30 USD to o2 + {IntentId: "t3", IntentType: intent.SendPublicPayment, InitiatorOwnerAccount: "o1", SendPublicPaymentMetadata: &intent.SendPublicPaymentMetadata{DestinationOwnerAccount: "o2", DestinationTokenAccount: "a2", Quantity: 300, ExchangeCurrency: currency.USD, ExchangeRate: 0.1, NativeAmount: 30, UsdMarketValue: 30}, MintAccount: "mint", State: intent.StateConfirmed}, + // SendPublicPayment: o2 sends to o1, o1 receives +20 USD + {IntentId: "t4", IntentType: intent.SendPublicPayment, InitiatorOwnerAccount: "o2", SendPublicPaymentMetadata: &intent.SendPublicPaymentMetadata{DestinationOwnerAccount: "o1", DestinationTokenAccount: "a1", Quantity: 200, ExchangeCurrency: currency.USD, ExchangeRate: 0.1, NativeAmount: 20, UsdMarketValue: 20}, MintAccount: "mint", State: intent.StateConfirmed}, + // Revoked intent should be ignored + {IntentId: "t5", IntentType: intent.ExternalDeposit, InitiatorOwnerAccount: "o1", ExternalDepositMetadata: &intent.ExternalDepositMetadata{DestinationTokenAccount: "a1", Quantity: 10000, ExchangeCurrency: currency.USD, ExchangeRate: 0.1, NativeAmount: 1000, UsdMarketValue: 1000}, MintAccount: "mint", State: intent.StateRevoked}, + // Different mint should not be included + {IntentId: "t6", IntentType: intent.ExternalDeposit, InitiatorOwnerAccount: "o1", ExternalDepositMetadata: &intent.ExternalDepositMetadata{DestinationTokenAccount: "a1", Quantity: 5000, ExchangeCurrency: currency.USD, ExchangeRate: 0.1, NativeAmount: 500, UsdMarketValue: 500}, MintAccount: "other_mint", State: intent.StateConfirmed}, + // SendPublicPayment to self: o1 sends and receives, net 0 for this transaction + {IntentId: "t7", IntentType: intent.SendPublicPayment, InitiatorOwnerAccount: "o1", SendPublicPaymentMetadata: &intent.SendPublicPaymentMetadata{DestinationOwnerAccount: "o1", DestinationTokenAccount: "a1", Quantity: 100, ExchangeCurrency: currency.USD, ExchangeRate: 0.1, NativeAmount: 10, UsdMarketValue: 10}, MintAccount: "mint", State: intent.StateConfirmed}, + } + + for _, record := range records { + require.NoError(t, s.Save(ctx, record)) + } + + // o1 cost basis: +100 (deposit) +50 (receive) -30 (send to o2) +20 (receive from o2) -10 (send to self) +10 (receive from self) = 140 + costBasis, err = s.GetUsdCostBasis(ctx, "o1", "mint") + require.NoError(t, err) + assert.EqualValues(t, 140, costBasis) + + // o2 cost basis: +30 (receive from o1) -20 (send to o1) = 10 + costBasis, err = s.GetUsdCostBasis(ctx, "o2", "mint") + require.NoError(t, err) + assert.EqualValues(t, 10, costBasis) + + // o1 cost basis for other_mint: +500 + costBasis, err = s.GetUsdCostBasis(ctx, "o1", "other_mint") + require.NoError(t, err) + assert.EqualValues(t, 500, costBasis) + + // Unknown owner has zero cost basis + costBasis, err = s.GetUsdCostBasis(ctx, "unknown", "mint") + require.NoError(t, err) + assert.EqualValues(t, 0, costBasis) + }) +} diff --git a/ocp/data/internal.go b/ocp/data/internal.go index bedd118..4031b5a 100644 --- a/ocp/data/internal.go +++ b/ocp/data/internal.go @@ -173,6 +173,7 @@ type DatabaseData interface { GetOriginalGiftCardIssuedIntent(ctx context.Context, giftCardVault string) (*intent.Record, error) GetGiftCardClaimedIntent(ctx context.Context, giftCardVault string) (*intent.Record, error) GetTransactedAmountForAntiMoneyLaundering(ctx context.Context, owner string, since time.Time) (uint64, float64, error) + GetUsdCostBasis(ctx context.Context, owner string, mint string) (float64, error) // Messaging // -------------------------------------------------------------------------------- @@ -639,6 +640,9 @@ func (dp *DatabaseProvider) SaveIntent(ctx context.Context, record *intent.Recor func (dp *DatabaseProvider) GetTransactedAmountForAntiMoneyLaundering(ctx context.Context, owner string, since time.Time) (uint64, float64, error) { return dp.intents.GetTransactedAmountForAntiMoneyLaundering(ctx, owner, since) } +func (dp *DatabaseProvider) GetUsdCostBasis(ctx context.Context, owner string, mint string) (float64, error) { + return dp.intents.GetUsdCostBasis(ctx, owner, mint) +} // Messaging // -------------------------------------------------------------------------------- From d76fff1318a657d9e03d1a7b2d843db449748526 Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Mon, 26 Jan 2026 15:00:41 -0500 Subject: [PATCH 2/2] GetTokenAccountInfos fetches USD cost basis from the DB --- ocp/rpc/account/server.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ocp/rpc/account/server.go b/ocp/rpc/account/server.go index 8c840a4..ab8aa36 100644 --- a/ocp/rpc/account/server.go +++ b/ocp/rpc/account/server.go @@ -457,9 +457,20 @@ func (s *server) getProtoAccountInfo(ctx context.Context, records *common.Accoun } } - usdCostBasis := 100.00 // todo: Mock test data + var usdCostBasis float64 if common.IsCoreMint(mintAccount) && common.IsCoreMintUsdStableCoin() { usdCostBasis = float64(prefetchedBalanceMetadata.value) / float64(common.CoreMintQuarksPerUnit) + } else { + switch records.General.AccountType { + case commonpb.AccountType_PRIMARY: + // todo: Assumes the structure that each user has exactly one primary account per mint + usdCostBasis, err = s.data.GetUsdCostBasis(ctx, ownerAccount.PublicKey().ToBase58(), mintAccount.PublicKey().ToBase58()) + if err != nil { + return nil, err + } + default: + usdCostBasis = 0 // Account type not supported + } } return &accountpb.TokenAccountInfo{