Skip to content

Commit 54eeb62

Browse files
committed
chore: add cleanup for expired non evm historical prices
1 parent b3c7eff commit 54eeb62

File tree

2 files changed

+129
-1
lines changed

2 files changed

+129
-1
lines changed

packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts

+94-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ const fakeHistoricalPrices: OnAssetHistoricalPriceResponse = {
9999
],
100100
},
101101
updateTime: 1737542312,
102-
expirationTime: 1737542312,
102+
// expirationTime is in 1Hour based on current Date.now()
103+
expirationTime: Date.now() + 1000 * 60 * 60,
103104
},
104105
};
105106

@@ -627,5 +628,97 @@ describe('MultichainAssetsRatesController', () => {
627628

628629
expect(snapHandler).toHaveBeenCalledTimes(1);
629630
});
631+
632+
it('does not clean up any of the prices if none of them have expired', async () => {
633+
const testCurrency = 'EUR';
634+
const testNativeAssetPrices = {
635+
intervals: {},
636+
updateTime: Date.now(),
637+
expirationTime: Date.now() + 1000, // not expired
638+
};
639+
const testTokenAssetPrices = {
640+
intervals: {},
641+
updateTime: Date.now(),
642+
expirationTime: Date.now() + 1000, // not expired
643+
};
644+
const { controller, messenger } = setupController({
645+
config: {
646+
state: {
647+
historicalPrices: {
648+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501': {
649+
[testCurrency]: testNativeAssetPrices,
650+
},
651+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:testToken1': {
652+
[testCurrency]: testTokenAssetPrices,
653+
},
654+
},
655+
},
656+
},
657+
});
658+
659+
const snapHandler = jest.fn().mockResolvedValue(fakeHistoricalPrices);
660+
messenger.registerActionHandler(
661+
'SnapController:handleRequest',
662+
snapHandler,
663+
);
664+
665+
await controller.fetchHistoricalPricesForAsset(
666+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
667+
);
668+
669+
expect(snapHandler).toHaveBeenCalledTimes(1);
670+
expect(controller.state.historicalPrices).toStrictEqual({
671+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501': {
672+
USD: fakeHistoricalPrices.historicalPrice,
673+
EUR: testNativeAssetPrices,
674+
},
675+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:testToken1': {
676+
EUR: testTokenAssetPrices,
677+
},
678+
});
679+
});
680+
681+
it('cleans up all historical prices that have expired', async () => {
682+
const testCurrency = 'EUR';
683+
const { controller, messenger } = setupController({
684+
config: {
685+
state: {
686+
historicalPrices: {
687+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501': {
688+
[testCurrency]: {
689+
intervals: {},
690+
updateTime: Date.now(),
691+
expirationTime: Date.now() - 1000, // expired
692+
},
693+
},
694+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:testToken1': {
695+
[testCurrency]: {
696+
intervals: {},
697+
updateTime: Date.now(),
698+
expirationTime: Date.now() - 1000, // expired
699+
},
700+
},
701+
},
702+
},
703+
},
704+
});
705+
706+
const snapHandler = jest.fn().mockResolvedValue(fakeHistoricalPrices);
707+
messenger.registerActionHandler(
708+
'SnapController:handleRequest',
709+
snapHandler,
710+
);
711+
712+
await controller.fetchHistoricalPricesForAsset(
713+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
714+
);
715+
716+
expect(snapHandler).toHaveBeenCalledTimes(1);
717+
expect(controller.state.historicalPrices).toStrictEqual({
718+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501': {
719+
USD: fakeHistoricalPrices.historicalPrice,
720+
},
721+
});
722+
});
630723
});
631724
});

packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts

+35
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,8 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro
389389
},
390390
};
391391
});
392+
// cleanup all historical prices that have expired
393+
this.#cleanupHistoricalPrices();
392394
} catch {
393395
throw new Error(
394396
`Failed to fetch historical prices for asset: ${asset}`,
@@ -399,6 +401,39 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro
399401
});
400402
}
401403

404+
#cleanupHistoricalPrices() {
405+
const allHistoricalPrices = this.state.historicalPrices;
406+
const cleanedHistoricalPrices =
407+
this.#removeExpiredEntries(allHistoricalPrices);
408+
this.update((state) => {
409+
state.historicalPrices = cleanedHistoricalPrices;
410+
});
411+
}
412+
413+
#removeExpiredEntries(
414+
data: Record<CaipAssetType, Record<string, HistoricalPrice>>,
415+
): Record<CaipAssetType, Record<string, HistoricalPrice>> {
416+
const now = Date.now();
417+
const result: Record<CaipAssetType, Record<string, HistoricalPrice>> = {};
418+
419+
Object.entries(data).forEach(([assetId, currencies]) => {
420+
const validCurrencies: Record<string, HistoricalPrice> = {};
421+
422+
Object.entries(currencies).forEach(([currency, details]) => {
423+
const exp = details.expirationTime;
424+
if (exp === undefined || exp > now) {
425+
validCurrencies[currency] = details;
426+
}
427+
});
428+
429+
if (Object.keys(validCurrencies).length > 0) {
430+
result[assetId as CaipAssetType] = validCurrencies;
431+
}
432+
});
433+
434+
return result;
435+
}
436+
402437
/**
403438
* Returns the array of CAIP-19 assets for the given account ID.
404439
* If none are found, returns an empty array.

0 commit comments

Comments
 (0)