Skip to content

fix: Price service caching logic for spot price#49

Open
stanleyyconsensys wants to merge 7 commits intomainfrom
fix/price-service
Open

fix: Price service caching logic for spot price#49
stanleyyconsensys wants to merge 7 commits intomainfrom
fix/price-service

Conversation

@stanleyyconsensys
Copy link
Copy Markdown
Collaborator

@stanleyyconsensys stanleyyconsensys commented May 4, 2026

Explanation

This PR adjusts PriceService.getSpotPrices caching to cache spot prices per asset ID + quote currency, enabling partial cache hits (fetch only missing assets) and batch cache operations.

before change
if the asset id is "1,2,3" and if the price for "1,2,3" is $1, $2, $3,
we cache the price in that way
key "1,2,3" = "$1,$2,$3"

if another request asset id "2,3" come in, we will have to request the API again, as we didnt cache "2,3"

after change
if the asset id is "1,2,3" and if the price for "1,2,3" is $1, $2, $3,
we cache the price in that way
key "1" = "$1"
key "2" = "$2"
key "3" = "$3"

if another request asset id "2,3" come in, we will no longer need to fetch from the api

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts PriceService.getSpotPrices caching to cache spot prices per asset ID + quote currency, enabling partial cache hits (fetch only missing assets) and batch cache operations.

Changes:

  • Replaced useCache-based spot price caching with a custom mget/mset strategy keyed per asset and vsCurrency.
  • Expanded PriceService unit tests to cover deduplication, default vsCurrency, partial hits, and cache isolation across currencies.
  • Enhanced the in-memory cache test fixture to implement mget/mset against the backing store map.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
packages/snap/src/services/price/PriceService.ts Implements per-asset spot price cache reads/writes using mget/mset and partial-miss fetching.
packages/snap/src/services/price/PriceService.test.ts Updates and expands spot price caching tests to match the new per-asset caching behavior.
packages/snap/src/services/cache/mocks/cache.fixtures.ts Implements mget/mset behavior in the test cache fixture so tests can validate batch caching.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/snap/src/services/price/PriceService.ts Outdated
Comment thread packages/snap/src/services/price/PriceService.ts Outdated
Comment thread packages/snap/src/services/price/PriceService.ts Outdated
Comment thread packages/snap/src/services/price/PriceService.ts
Comment on lines +72 to +101
const setupTest = () => {
const getSpotPricesSpy = jest.spyOn(
PriceApiClient.prototype,
'getSpotPrices',
);
getSpotPricesSpy.mockReset();
getSpotPricesSpy.mockResolvedValue({ [stellarClassicUsdc]: null });

const getFiatExchangeRatesSpy = jest.spyOn(
PriceApiClient.prototype,
'getFiatExchangeRates',
);
getFiatExchangeRatesSpy.mockReset();
getFiatExchangeRatesSpy.mockResolvedValue(fiatExchangeRatesBody);

const getHistoricalPricesSpy = jest.spyOn(
PriceApiClient.prototype,
'getHistoricalPrices',
);
getHistoricalPricesSpy.mockReset();
getHistoricalPricesSpy.mockResolvedValue(
GET_HISTORICAL_PRICES_RESPONSE_NULL_OBJECT,
);

return {
getSpotPricesSpy,
getFiatExchangeRatesSpy,
getHistoricalPricesSpy,
};
};
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restoreAllMocks is no need, as our jest setup reseted mock

vsCurrency: string,
) =>
`PriceService:getSpotPrices:${JSON.stringify(serialize(assetIds))}:${JSON.stringify(serialize(vsCurrency))}`;
const SPOT_PRICES_CACHE_KEY_PREFIX = 'PriceApiClient:getSpotPrices' as const;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with the other cache namespaces in this class (PriceService:getFiatExchangeRates, PriceService:getHistoricalPrices), rename the prefix from PriceApiClient:getSpotPrices to PriceService:getSpotPrices

@@ -70,32 +69,40 @@ const cacheKeyHistoricalPrices = (params: {
}) => `PriceService:getHistoricalPrices:${JSON.stringify(serialize(params))}`;

describe('PriceService', () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: I think better to add a top-level afterEach for any future test that forgets setupTest()

afterEach(() => {
    jest.restoreAllMocks();
  });

Copy link
Copy Markdown
Collaborator Author

@stanleyyconsensys stanleyyconsensys May 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dont need, i have set the restore on jest config level

and i try to remove all after each, before each to align the MM test standard

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants