diff --git a/app/services/contract_metadata_service.py b/app/services/contract_metadata_service.py index aca3e8b..642e9e0 100644 --- a/app/services/contract_metadata_service.py +++ b/app/services/contract_metadata_service.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: FSL-1.1-MIT import enum import logging from dataclasses import dataclass @@ -22,6 +23,7 @@ from app.config import settings from app.datasources.cache.redis import get_redis from app.datasources.db.models import Abi, AbiSource, Contract +from app.services.data_decoder import get_data_decoder_service logger = logging.getLogger(__name__) @@ -199,6 +201,14 @@ async def process_contract_metadata( contract_metadata.metadata.implementation ) + # Evict stale per-contract ABI cache entries so the decoder + # picks up the newly linked ABI on the next request. + decoder = await get_data_decoder_service() + decoder.invalidate_contract_abi_cache( + contract_metadata.address, + contract_metadata.chain_id, + ) + contract.fetch_retries += 1 await contract.update() return bool(contract_metadata.metadata) diff --git a/app/services/data_decoder.py b/app/services/data_decoder.py index 015e1d8..e517ea7 100644 --- a/app/services/data_decoder.py +++ b/app/services/data_decoder.py @@ -175,7 +175,7 @@ async def get_supported_abis(self) -> AsyncIterator[ABI]: async def get_multisend_abis(self) -> AsyncIterator[ABI]: yield get_multi_send_contract(self.dummy_w3).abi - @alru_cache(maxsize=2048) + @alru_cache(maxsize=2048, ttl=600) async def get_contract_abi( self, address: Address, @@ -190,7 +190,7 @@ async def get_contract_abi( """ return await Contract.get_abi_by_contract_address(HexBytes(address), chain_id) - @alru_cache(maxsize=2048) + @alru_cache(maxsize=2048, ttl=600) async def get_contract_abi_selectors_with_functions( self, address: Address, chain_id: int | None ) -> dict[bytes, ABIFunction] | None: @@ -498,6 +498,24 @@ async def get_decoding_accuracy( return DecodingAccuracyEnum.PARTIAL_MATCH return DecodingAccuracyEnum.ONLY_FUNCTION_MATCH + def invalidate_contract_abi_cache( + self, address: Address | ChecksumAddress, chain_id: int | None + ) -> None: + """ + Evict a specific contract from both per-contract ABI caches. + + Call this whenever a contract's ABI is updated in the database so + that the next decode request re-fetches the latest ABI rather than + serving stale data from the in-memory cache. + + :param address: Contract address + :param chain_id: Chain id for the contract + """ + self.get_contract_abi.cache_invalidate(self, address, chain_id) + self.get_contract_abi_selectors_with_functions.cache_invalidate( + self, address, chain_id + ) + async def add_abi(self, abi: ABI) -> bool: """ Add a new abi without rebuilding the entire decoder