|
53 | 53 | from dipdup.exceptions import InitializationRequiredError |
54 | 54 | from dipdup.exceptions import ReindexingRequiredError |
55 | 55 | from dipdup.models import Contract |
| 56 | +from dipdup.models import ContractMetadata |
56 | 57 | from dipdup.models import Index |
57 | 58 | from dipdup.models import ReindexingReason |
58 | 59 | from dipdup.models import Schema |
| 60 | +from dipdup.models import TokenMetadata |
59 | 61 | from dipdup.utils import FormattedLogger |
60 | 62 | from dipdup.utils import slowdown |
61 | 63 | from dipdup.utils.database import execute_sql_scripts |
|
68 | 70 | pending_hooks: Deque[Awaitable[None]] = deque() |
69 | 71 |
|
70 | 72 |
|
| 73 | +class MetadataCursor: |
| 74 | + _contract = 0 |
| 75 | + _token = 0 |
| 76 | + |
| 77 | + def __new__(cls): |
| 78 | + raise NotImplementedError |
| 79 | + |
| 80 | + @classmethod |
| 81 | + async def initialize(cls) -> None: |
| 82 | + if last_contract := await ContractMetadata.filter().order_by('-update_id').first(): |
| 83 | + cls._contract = last_contract.update_id |
| 84 | + if last_token := await TokenMetadata.filter().order_by('-update_id').first(): |
| 85 | + cls._token = last_token.update_id |
| 86 | + |
| 87 | + @classmethod |
| 88 | + def contract(cls) -> int: |
| 89 | + cls._contract += 1 |
| 90 | + return cls._contract |
| 91 | + |
| 92 | + @classmethod |
| 93 | + def token(cls) -> int: |
| 94 | + cls._token += 1 |
| 95 | + return cls._token |
| 96 | + |
| 97 | + |
71 | 98 | # TODO: Dataclasses are cool, everyone loves them. Resolve issue with pydantic serialization. |
72 | 99 | class DipDupContext: |
73 | 100 | def __init__( |
@@ -216,6 +243,41 @@ async def spawn_index(self, name: str, state: Optional[Index] = None) -> None: |
216 | 243 | # NOTE: IndexDispatcher will handle further initialization when it's time |
217 | 244 | pending_indexes.append(index) |
218 | 245 |
|
| 246 | + async def update_contract_metadata( |
| 247 | + self, |
| 248 | + network: str, |
| 249 | + address: str, |
| 250 | + metadata: Dict[str, Any], |
| 251 | + ) -> None: |
| 252 | + if not self.config.advanced.metadata_interface: |
| 253 | + return |
| 254 | + update_id = MetadataCursor.contract() |
| 255 | + await ContractMetadata.update_or_create( |
| 256 | + network=network, |
| 257 | + contract=address, |
| 258 | + defaults={'metadata': metadata, 'update_id': update_id}, |
| 259 | + ) |
| 260 | + |
| 261 | + async def update_token_metadata( |
| 262 | + self, |
| 263 | + network: str, |
| 264 | + address: str, |
| 265 | + token_id: str, |
| 266 | + metadata: Dict[str, Any], |
| 267 | + ) -> None: |
| 268 | + if not self.config.advanced.metadata_interface: |
| 269 | + return |
| 270 | + if not all(str.isdigit(c) for c in token_id): |
| 271 | + raise ValueError('`token_id` must be a number') |
| 272 | + |
| 273 | + update_id = MetadataCursor.token() |
| 274 | + await TokenMetadata.update_or_create( |
| 275 | + network=network, |
| 276 | + contract=address, |
| 277 | + token_id=token_id, |
| 278 | + defaults={'metadata': metadata, 'update_id': update_id}, |
| 279 | + ) |
| 280 | + |
219 | 281 | def _get_datasource(self, name: str, type_: Type[DatasourceT]) -> DatasourceT: |
220 | 282 | datasource = self.datasources.get(name) |
221 | 283 | if not datasource: |
|
0 commit comments