diff --git a/docs/golem_base_sdk.html b/docs/golem_base_sdk.html index 7eb4202..32d5fec 100644 --- a/docs/golem_base_sdk.html +++ b/docs/golem_base_sdk.html @@ -126,574 +126,571 @@

-
  1#! /usr/bin/env python
+                        
  1"""GolemBase Python SDK"""
   2
-  3"""GolemBase Python SDK"""
-  4
-  5import asyncio
-  6import base64
-  7import logging
-  8import logging.config
-  9import typing
- 10from typing import (
- 11    AsyncGenerator,
- 12    Awaitable,
- 13    Callable,
- 14    Iterable,
- 15    List,
- 16    Optional,
- 17    Sequence,
- 18    Set,
- 19)
- 20
- 21from eth_typing import ChecksumAddress, HexStr
- 22from web3 import AsyncWeb3, WebSocketProvider
- 23from web3.contract import AsyncContract
- 24from web3.exceptions import ProviderConnectionError
- 25from web3.method import Method, default_root_munger
- 26from web3.middleware import SignAndSendRawMiddlewareBuilder
- 27from web3.types import LogReceipt, RPCEndpoint, TxReceipt
- 28from web3.utils.subscriptions import (
- 29    LogsSubscription,
- 30    LogsSubscriptionContext,
- 31)
- 32
- 33from .constants import (
- 34    GOLEM_BASE_ABI,
- 35    STORAGE_ADDRESS,
- 36)
- 37from .types import (
- 38    Address,
- 39    Annotation,
- 40    CreateEntityReturnType,
- 41    EntityKey,
- 42    EntityMetadata,
- 43    ExtendEntityReturnType,
- 44    GenericBytes,
- 45    GolemBaseCreate,
- 46    GolemBaseDelete,
- 47    GolemBaseExtend,
- 48    GolemBaseTransaction,
- 49    GolemBaseTransactionReceipt,
- 50    GolemBaseUpdate,
- 51    QueryEntitiesResult,
- 52    UpdateEntityReturnType,
- 53)
- 54from .utils import parse_legacy_btl_extended_log, rlp_encode_transaction
+  3import asyncio
+  4import base64
+  5import logging
+  6import logging.config
+  7import typing
+  8from typing import (
+  9    AsyncGenerator,
+ 10    Awaitable,
+ 11    Callable,
+ 12    List,
+ 13    Optional,
+ 14    Sequence,
+ 15    Set,
+ 16)
+ 17
+ 18from eth_typing import ChecksumAddress, HexStr
+ 19from web3 import AsyncWeb3, WebSocketProvider
+ 20from web3.contract import AsyncContract
+ 21from web3.exceptions import ProviderConnectionError
+ 22from web3.method import Method, default_root_munger
+ 23from web3.middleware import SignAndSendRawMiddlewareBuilder
+ 24from web3.types import LogReceipt, RPCEndpoint, TxReceipt
+ 25from web3.utils.subscriptions import (
+ 26    LogsSubscription,
+ 27    LogsSubscriptionContext,
+ 28)
+ 29
+ 30from .constants import (
+ 31    GOLEM_BASE_ABI,
+ 32    STORAGE_ADDRESS,
+ 33)
+ 34from .types import (
+ 35    Address,
+ 36    Annotation,
+ 37    CreateEntityReturnType,
+ 38    EntityKey,
+ 39    EntityMetadata,
+ 40    ExtendEntityReturnType,
+ 41    GenericBytes,
+ 42    GolemBaseCreate,
+ 43    GolemBaseDelete,
+ 44    GolemBaseExtend,
+ 45    GolemBaseTransaction,
+ 46    GolemBaseTransactionReceipt,
+ 47    GolemBaseUpdate,
+ 48    QueryEntitiesResult,
+ 49    UpdateEntityReturnType,
+ 50)
+ 51from .utils import parse_legacy_btl_extended_log, rlp_encode_transaction
+ 52
+ 53
+ 54__version__ = "0.0.2"
  55
  56
- 57__version__ = "0.0.2"
- 58
- 59
- 60logger = logging.getLogger(__name__)
- 61"""
- 62@private
- 63"""
- 64
- 65
- 66class GolemBaseClient:
- 67    """
- 68    The Golem Base client used to interact with Golem Base.
- 69    Many useful methods are implemented directly on this type, while more
- 70    generic ethereum methods can be accessed through the underlying
- 71    web3 client that you can access with the
- 72    `GolemBaseClient.http_client()`
- 73    method.
- 74    """
- 75
- 76    _http_client: AsyncWeb3
- 77    _ws_client: AsyncWeb3
- 78    _golem_base_contract: AsyncContract
- 79    _background_tasks: Set[asyncio.Task]
- 80
- 81    @staticmethod
- 82    async def create(
- 83        rpc_url: str, ws_url: str, private_key: Sequence[bytes]
- 84    ) -> "GolemBaseClient":
- 85        """
- 86        Static method to create a `GolemBaseClient` instance,
- 87        this is the preferred method to create an instance.
- 88        """
- 89        ws_client = await AsyncWeb3(WebSocketProvider(ws_url))
- 90        return GolemBaseClient(rpc_url, ws_client, private_key)
- 91
- 92    def __init__(
- 93        self, rpc_url: str, ws_client: AsyncWeb3, private_key: Sequence[bytes]
- 94    ) -> None:
- 95        self._http_client = GolemBaseClient._create_client(rpc_url)
- 96        self._ws_client = ws_client
- 97
- 98        # Keep references to async tasks we created
- 99        self._background_tasks = set()
-100
-101        def is_connected(client) -> Callable[[bool], Awaitable[bool]]:
-102            async def inner(show_traceback: bool) -> bool:
-103                try:
-104                    logger.debug("Calling eth_blockNumber to test connectivity...")
-105                    await client.eth.get_block_number()
-106                    return True
-107                except (OSError, ProviderConnectionError) as e:
-108                    logger.debug(
-109                        "Problem connecting to provider", exc_info=show_traceback
-110                    )
-111                    if show_traceback:
-112                        raise ProviderConnectionError(
-113                            "Problem connecting to provider"
-114                        ) from e
-115                    return False
-116
-117            return inner
-118
-119        # The default is_connected method calls web3_clientVersion, but the web3
-120        # API is not enabled on all our nodes, so let's monkey patch this to call
-121        # eth_getBlockNumber instead.
-122        # The method on the provider is usually not called directly, instead you
-123        # can call the eponymous method on the client, which will delegate to the
-124        # provider.
-125        setattr(
-126            self.http_client().provider,
-127            "is_connected",
-128            is_connected(self.http_client()),
-129        )
-130
-131        # Allow caching of certain methods to improve performance
-132        self.http_client().provider.cache_allowed_requests = True
-133
-134        # Set up the ethereum account
-135        self.account = self.http_client().eth.account.from_key(private_key)
-136        # Inject a middleware that will sign transactions with the account that we created
-137        self.http_client().middleware_onion.inject(
-138            # pylint doesn't detect nested @curry annotations properly...
-139            # pylint: disable=no-value-for-parameter
-140            SignAndSendRawMiddlewareBuilder.build(self.account),
-141            layer=0,
-142        )
-143        # Set the account as the default, so we don't need to specify the from field
-144        # every time
-145        self.http_client().eth.default_account = self.account.address
-146        logger.debug("Using account: %s", self.account.address)
-147
-148        # https://github.com/pylint-dev/pylint/issues/3162
-149        # pylint: disable=no-member
-150        self.golem_base_contract = self.http_client().eth.contract(
-151            address=STORAGE_ADDRESS.as_address(),
-152            abi=GOLEM_BASE_ABI,
-153        )
-154        for event in self.golem_base_contract.all_events():
-155            logger.debug(
-156                "Registered event %s with hash %s", event.signature, event.topic
-157            )
-158
-159    @staticmethod
-160    def _create_client(rpc_url: str) -> AsyncWeb3:
-161        client = AsyncWeb3(
-162            AsyncWeb3.AsyncHTTPProvider(rpc_url, request_kwargs={"timeout": 60}),
-163        )
-164        client.eth.attach_methods(
-165            {
-166                "get_storage_value": Method(
-167                    json_rpc_method=RPCEndpoint("golembase_getStorageValue"),
-168                    mungers=[default_root_munger],
-169                ),
-170                "get_entity_metadata": Method(
-171                    json_rpc_method=RPCEndpoint("golembase_getEntityMetaData"),
-172                    mungers=[default_root_munger],
-173                ),
-174                "get_entities_to_expire_at_block": Method(
-175                    json_rpc_method=RPCEndpoint("golembase_getEntitiesToExpireAtBlock"),
-176                    mungers=[default_root_munger],
-177                ),
-178                "get_entity_count": Method(
-179                    json_rpc_method=RPCEndpoint("golembase_getEntityCount"),
-180                    mungers=[default_root_munger],
-181                ),
-182                "get_all_entity_keys": Method(
-183                    json_rpc_method=RPCEndpoint("golembase_getAllEntityKeys"),
-184                    mungers=[default_root_munger],
-185                ),
-186                "get_entities_of_owner": Method(
-187                    json_rpc_method=RPCEndpoint("golembase_getEntitiesOfOwner"),
-188                    mungers=[default_root_munger],
-189                ),
-190                "query_entities": Method(
-191                    json_rpc_method=RPCEndpoint("golembase_queryEntities"),
-192                    mungers=[default_root_munger],
-193                ),
-194            }
-195        )
-196        return client
-197
-198    def http_client(self):
-199        """
-200        Get the underlying web3 http client
-201        """
-202        return self._http_client
-203
-204    def ws_client(self) -> AsyncWeb3:
-205        """
-206        Get the underlying web3 websocket client
-207        """
-208        return self._ws_client
-209
-210    async def is_connected(self) -> bool:
-211        """
-212        Check whether the client's underlying http client is connected
-213        """
-214        return await self.http_client().is_connected()
-215
-216    async def disconnect(self) -> None:
-217        """
-218        Disconnect both the underlying http and ws clients and
-219        unsubscribe from all subscriptions
-220        """
-221        await self.http_client().provider.disconnect()
-222        await self.ws_client().subscription_manager.unsubscribe_all()
-223        await self.ws_client().provider.disconnect()
-224
-225    def get_account_address(self) -> ChecksumAddress:
-226        """
-227        Get the address associated with the private key that this client
-228        was created with
-229        """
-230        return self.account.address
-231
-232    async def get_storage_value(self, entity_key: EntityKey) -> bytes:
-233        """
-234        Get the storage value stored in the given entity
-235        """
-236        return base64.b64decode(
-237            await self.http_client().eth.get_storage_value(entity_key.as_hex_string())
-238        )
-239
-240    async def get_entity_metadata(self, entity_key: EntityKey) -> EntityMetadata:
-241        """
-242        Get the metadata of the given entity
-243        """
-244        metadata = await self.http_client().eth.get_entity_metadata(
-245            entity_key.as_hex_string()
-246        )
-247
-248        return EntityMetadata(
-249            entity_key=entity_key,
-250            owner=Address(GenericBytes.from_hex_string(metadata.owner)),
-251            expires_at_block=metadata.expiresAtBlock,
-252            string_annotations=list(
-253                map(
-254                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
-255                    metadata.stringAnnotations,
-256                )
-257            ),
-258            numeric_annotations=list(
-259                map(
-260                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
-261                    metadata.numericAnnotations,
-262                )
-263            ),
-264        )
-265
-266    async def get_entities_to_expire_at_block(
-267        self, block_number: int
-268    ) -> Iterable[EntityKey]:
-269        """
-270        Get all entities that will expire at the given block
-271        """
-272        return list(
-273            map(
-274                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
-275                await self.http_client().eth.get_entities_to_expire_at_block(
-276                    block_number
-277                ),
-278            )
-279        )
-280
-281    async def get_entity_count(self) -> int:
-282        """
-283        Get the total entity count in Golem Base
-284        """
-285        return await self.http_client().eth.get_entity_count()
-286
-287    async def get_all_entity_keys(self) -> Sequence[EntityKey]:
-288        """
-289        Get all entity keys in Golem Base
-290        """
-291        return list(
-292            map(
-293                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
-294                await self.http_client().eth.get_all_entity_keys(),
-295            )
-296        )
-297
-298    async def get_entities_of_owner(self, owner: Address) -> Sequence[EntityKey]:
-299        """
-300        Get all the entities owned by the given address
-301        """
-302        return list(
-303            map(
-304                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
-305                # https://github.com/pylint-dev/pylint/issues/3162
-306                # pylint: disable=no-member
-307                await self.http_client().eth.get_entities_of_owner(owner),
-308            )
-309        )
-310
-311    async def query_entities(self, query: str) -> Sequence[QueryEntitiesResult]:
-312        """
-313        Get all entities that satisfy the given Golem Base query
-314        """
-315        return list(
-316            map(
-317                lambda result: QueryEntitiesResult(
-318                    entity_key=result.key, storage_value=base64.b64decode(result.value)
-319                ),
-320                await self.http_client().eth.query_entities(query),
-321            )
-322        )
-323
-324    async def create_entities(
-325        self,
-326        creates: Sequence[GolemBaseCreate],
-327    ) -> Iterable[CreateEntityReturnType]:
-328        """
-329        Create entities in Golem Base
-330        """
-331        return (await self.send_transaction(creates=creates)).creates
-332
-333    async def update_entities(
-334        self,
-335        updates: Sequence[GolemBaseUpdate],
-336    ) -> Iterable[UpdateEntityReturnType]:
-337        """
-338        Update entities in Golem Base
-339        """
-340        return (await self.send_transaction(updates=updates)).updates
-341
-342    async def delete_entities(
-343        self,
-344        deletes: Sequence[GolemBaseDelete],
-345    ) -> Iterable[EntityKey]:
-346        """
-347        Delete entities from Golem Base
-348        """
-349        return (await self.send_transaction(deletes=deletes)).deletes
-350
-351    async def extend_entities(
-352        self,
-353        extensions: Sequence[GolemBaseExtend],
-354    ) -> Iterable[ExtendEntityReturnType]:
-355        """
-356        Extend the BTL of entities in Golem Base
-357        """
-358        return (await self.send_transaction(extensions=extensions)).extensions
-359
-360    async def send_transaction(
-361        self,
-362        creates: Optional[Sequence[GolemBaseCreate]] = None,
-363        updates: Optional[Sequence[GolemBaseUpdate]] = None,
-364        deletes: Optional[Sequence[GolemBaseDelete]] = None,
-365        extensions: Optional[Sequence[GolemBaseExtend]] = None,
-366    ) -> GolemBaseTransactionReceipt:
-367        """
-368        Send a generic transaction to Golem Base.
-369        This transaction can contain multiple create, update, delete and
-370        extend operations
-371        """
-372        tx = GolemBaseTransaction(
-373            creates,
-374            updates,
-375            deletes,
-376            extensions,
-377        )
-378        return await self._send_gb_transaction(tx)
-379
-380    async def _process_golem_base_log_receipt(
-381        self,
-382        log_receipt: LogReceipt,
-383    ) -> GolemBaseTransactionReceipt:
-384        # Read the first entry of the topics array,
-385        # which is the hash of the event signature, identifying the event
-386        topic = AsyncWeb3.to_hex(log_receipt["topics"][0])
-387        # Look up the corresponding event
-388        # If there is no such event in the ABI, it probably needs to be added
-389        event = self.golem_base_contract.get_event_by_topic(topic)
-390        # Use the event to process the whole log
-391        event_data = event.process_log(log_receipt)
-392
-393        creates: List[CreateEntityReturnType] = []
-394        updates: List[UpdateEntityReturnType] = []
-395        deletes: List[EntityKey] = []
-396        extensions: List[ExtendEntityReturnType] = []
-397
-398        match event_data["event"]:
-399            case "GolemBaseStorageEntityCreated":
-400                creates.append(
-401                    CreateEntityReturnType(
-402                        expiration_block=event_data["args"]["expirationBlock"],
-403                        entity_key=EntityKey(
-404                            GenericBytes(
-405                                event_data["args"]["entityKey"].to_bytes(32, "big")
-406                            )
-407                        ),
-408                    )
-409                )
-410            case "GolemBaseStorageEntityUpdated":
-411                updates.append(
-412                    UpdateEntityReturnType(
-413                        expiration_block=event_data["args"]["expirationBlock"],
-414                        entity_key=EntityKey(
-415                            GenericBytes(
-416                                event_data["args"]["entityKey"].to_bytes(32, "big")
-417                            )
-418                        ),
-419                    )
-420                )
-421            case "GolemBaseStorageEntityDeleted":
-422                deletes.append(
-423                    EntityKey(
-424                        GenericBytes(
-425                            event_data["args"]["entityKey"].to_bytes(32, "big")
-426                        ),
-427                    )
-428                )
-429            case "GolemBaseStorageEntityBTLExtended":
-430                extensions.append(
-431                    ExtendEntityReturnType(
-432                        old_expiration_block=event_data["args"]["old_expirationBlock"],
-433                        new_expiration_block=event_data["args"]["new_expirationBlock"],
-434                        entity_key=EntityKey(
-435                            GenericBytes(
-436                                event_data["args"]["entityKey"].to_bytes(32, "big")
-437                            )
-438                        ),
-439                    )
-440                )
-441            # This is only here for backwards compatibility and can be removed
-442            # once we undeploy kaolin.
-443            case (
-444                "GolemBaseStorageEntityBTLExptended"
-445                | "GolemBaseStorageEntityTTLExptended"
-446            ):
-447                extensions.append(parse_legacy_btl_extended_log(log_receipt))
-448            case other:
-449                raise ValueError(f"Unknown event type: {other}")
-450
-451        return GolemBaseTransactionReceipt(
-452            creates=creates,
-453            updates=updates,
-454            deletes=deletes,
-455            extensions=extensions,
-456        )
-457
-458    async def _process_golem_base_receipt(
-459        self, receipt: TxReceipt
-460    ) -> GolemBaseTransactionReceipt:
-461        # There doesn't seem to be a method for this in the web3 lib.
-462        # The only option in the lib is to iterate over the events in the ABI
-463        # and call process_receipt on each of them to try and decode the logs.
-464        # This is inefficient though compared to reading the actual topic signature
-465        # and immediately selecting the right event from the ABI, which is what
-466        # we do here.
-467        async def process_receipt(
-468            receipt: TxReceipt,
-469        ) -> AsyncGenerator[GolemBaseTransactionReceipt, None]:
-470            for log in receipt["logs"]:
-471                yield await self._process_golem_base_log_receipt(log)
-472
-473        creates: List[CreateEntityReturnType] = []
-474        updates: List[UpdateEntityReturnType] = []
-475        deletes: List[EntityKey] = []
-476        extensions: List[ExtendEntityReturnType] = []
-477
-478        async for res in process_receipt(receipt):
-479            creates.extend(res.creates)
-480            updates.extend(res.updates)
-481            deletes.extend(res.deletes)
-482            extensions.extend(res.extensions)
-483
-484        return GolemBaseTransactionReceipt(
-485            creates=creates,
-486            updates=updates,
-487            deletes=deletes,
-488            extensions=extensions,
-489        )
-490
-491    async def _send_gb_transaction(
-492        self, tx: GolemBaseTransaction
-493    ) -> GolemBaseTransactionReceipt:
-494        txhash = await self.http_client().eth.send_transaction(
-495            {
-496                # https://github.com/pylint-dev/pylint/issues/3162
-497                # pylint: disable=no-member
-498                "to": STORAGE_ADDRESS.as_address(),
-499                "value": AsyncWeb3.to_wei(0, "ether"),
-500                "data": rlp_encode_transaction(tx),
-501            }
-502        )
-503        receipt = await self.http_client().eth.wait_for_transaction_receipt(txhash)
-504        return await self._process_golem_base_receipt(receipt)
-505
-506    async def watch_logs(
-507        self,
-508        create_callback: Callable[[CreateEntityReturnType], None],
-509        update_callback: Callable[[UpdateEntityReturnType], None],
-510        delete_callback: Callable[[EntityKey], None],
-511        extend_callback: Callable[[ExtendEntityReturnType], None],
-512    ) -> None:
-513        """
-514        Subscribe to events on Golem Base.
-515        You can pass in four different callbacks, and the right one will
-516        be invoked for every create, update, delete, and extend operation.
-517        """
-518
-519        async def log_handler(
-520            handler_context: LogsSubscriptionContext,
-521        ) -> None:
-522            # We only use this handler for log receipts
-523            # TypeDicts cannot be checked at runtime
-524            log_receipt = typing.cast(LogReceipt, handler_context.result)
-525            logger.debug("New log: %s", log_receipt)
-526            res = await self._process_golem_base_log_receipt(log_receipt)
-527
-528            for create in res.creates:
-529                create_callback(create)
-530            for update in res.updates:
-531                update_callback(update)
-532            for key in res.deletes:
-533                delete_callback(key)
-534            for extension in res.extensions:
-535                extend_callback(extension)
-536
-537        def create_subscription(topic: HexStr) -> LogsSubscription:
-538            return LogsSubscription(
-539                label=f"Golem Base subscription to topic {topic}",
-540                address=self.golem_base_contract.address,
-541                topics=[topic],
-542                handler=log_handler,
-543                # optional `handler_context` args to help parse a response
-544                handler_context={},
-545            )
-546
-547        async def handle_subscriptions() -> None:
-548            await self._ws_client.subscription_manager.subscribe(
-549                list(
-550                    map(
-551                        lambda event: create_subscription(event.topic),
-552                        self.golem_base_contract.all_events(),
-553                    )
-554                ),
-555            )
-556            # handle subscriptions via configured handlers:
-557            await self.ws_client().subscription_manager.handle_subscriptions()
-558
-559        # Create a long running task to handle subscriptions that we can run on
-560        # the asyncio event loop
-561        task = asyncio.create_task(handle_subscriptions())
-562        self._background_tasks.add(task)
-563
-564        def task_done(task: asyncio.Task) -> None:
-565            logger.info("Subscription background task done, removing...")
-566            self._background_tasks.discard(task)
-567
-568        task.add_done_callback(task_done)
+ 57logger = logging.getLogger(__name__)
+ 58"""
+ 59@private
+ 60"""
+ 61
+ 62
+ 63class GolemBaseClient:
+ 64    """
+ 65    The Golem Base client used to interact with Golem Base.
+ 66    Many useful methods are implemented directly on this type, while more
+ 67    generic ethereum methods can be accessed through the underlying
+ 68    web3 client that you can access with the
+ 69    `GolemBaseClient.http_client()`
+ 70    method.
+ 71    """
+ 72
+ 73    _http_client: AsyncWeb3
+ 74    _ws_client: AsyncWeb3
+ 75    _golem_base_contract: AsyncContract
+ 76    _background_tasks: Set[asyncio.Task]
+ 77
+ 78    @staticmethod
+ 79    async def create(
+ 80        rpc_url: str, ws_url: str, private_key: bytes
+ 81    ) -> "GolemBaseClient":
+ 82        """
+ 83        Static method to create a `GolemBaseClient` instance,
+ 84        this is the preferred method to create an instance.
+ 85        """
+ 86        ws_client = await AsyncWeb3(WebSocketProvider(ws_url))
+ 87        return GolemBaseClient(rpc_url, ws_client, private_key)
+ 88
+ 89    def __init__(self, rpc_url: str, ws_client: AsyncWeb3, private_key: bytes) -> None:
+ 90        self._http_client = GolemBaseClient._create_client(rpc_url)
+ 91        self._ws_client = ws_client
+ 92
+ 93        # Keep references to async tasks we created
+ 94        self._background_tasks = set()
+ 95
+ 96        def is_connected(client) -> Callable[[bool], Awaitable[bool]]:
+ 97            async def inner(show_traceback: bool) -> bool:
+ 98                try:
+ 99                    logger.debug("Calling eth_blockNumber to test connectivity...")
+100                    await client.eth.get_block_number()
+101                    return True
+102                except (OSError, ProviderConnectionError) as e:
+103                    logger.debug(
+104                        "Problem connecting to provider", exc_info=show_traceback
+105                    )
+106                    if show_traceback:
+107                        raise ProviderConnectionError(
+108                            "Problem connecting to provider"
+109                        ) from e
+110                    return False
+111
+112            return inner
+113
+114        # The default is_connected method calls web3_clientVersion, but the web3
+115        # API is not enabled on all our nodes, so let's monkey patch this to call
+116        # eth_getBlockNumber instead.
+117        # The method on the provider is usually not called directly, instead you
+118        # can call the eponymous method on the client, which will delegate to the
+119        # provider.
+120        setattr(
+121            self.http_client().provider,
+122            "is_connected",
+123            is_connected(self.http_client()),
+124        )
+125
+126        # Allow caching of certain methods to improve performance
+127        self.http_client().provider.cache_allowed_requests = True
+128
+129        # Set up the ethereum account
+130        self.account = self.http_client().eth.account.from_key(private_key)
+131        # Inject a middleware that will sign transactions with the account that we created
+132        self.http_client().middleware_onion.inject(
+133            # pylint doesn't detect nested @curry annotations properly...
+134            # pylint: disable=no-value-for-parameter
+135            SignAndSendRawMiddlewareBuilder.build(self.account),
+136            layer=0,
+137        )
+138        # Set the account as the default, so we don't need to specify the from field
+139        # every time
+140        self.http_client().eth.default_account = self.account.address
+141        logger.debug("Using account: %s", self.account.address)
+142
+143        # https://github.com/pylint-dev/pylint/issues/3162
+144        # pylint: disable=no-member
+145        self.golem_base_contract = self.http_client().eth.contract(
+146            address=STORAGE_ADDRESS.as_address(),
+147            abi=GOLEM_BASE_ABI,
+148        )
+149        for event in self.golem_base_contract.all_events():
+150            logger.debug(
+151                "Registered event %s with hash %s", event.signature, event.topic
+152            )
+153
+154    @staticmethod
+155    def _create_client(rpc_url: str) -> AsyncWeb3:
+156        client = AsyncWeb3(
+157            AsyncWeb3.AsyncHTTPProvider(rpc_url, request_kwargs={"timeout": 60}),
+158        )
+159        client.eth.attach_methods(
+160            {
+161                "get_storage_value": Method(
+162                    json_rpc_method=RPCEndpoint("golembase_getStorageValue"),
+163                    mungers=[default_root_munger],
+164                ),
+165                "get_entity_metadata": Method(
+166                    json_rpc_method=RPCEndpoint("golembase_getEntityMetaData"),
+167                    mungers=[default_root_munger],
+168                ),
+169                "get_entities_to_expire_at_block": Method(
+170                    json_rpc_method=RPCEndpoint("golembase_getEntitiesToExpireAtBlock"),
+171                    mungers=[default_root_munger],
+172                ),
+173                "get_entity_count": Method(
+174                    json_rpc_method=RPCEndpoint("golembase_getEntityCount"),
+175                    mungers=[default_root_munger],
+176                ),
+177                "get_all_entity_keys": Method(
+178                    json_rpc_method=RPCEndpoint("golembase_getAllEntityKeys"),
+179                    mungers=[default_root_munger],
+180                ),
+181                "get_entities_of_owner": Method(
+182                    json_rpc_method=RPCEndpoint("golembase_getEntitiesOfOwner"),
+183                    mungers=[default_root_munger],
+184                ),
+185                "query_entities": Method(
+186                    json_rpc_method=RPCEndpoint("golembase_queryEntities"),
+187                    mungers=[default_root_munger],
+188                ),
+189            }
+190        )
+191        return client
+192
+193    def http_client(self):
+194        """
+195        Get the underlying web3 http client
+196        """
+197        return self._http_client
+198
+199    def ws_client(self) -> AsyncWeb3:
+200        """
+201        Get the underlying web3 websocket client
+202        """
+203        return self._ws_client
+204
+205    async def is_connected(self) -> bool:
+206        """
+207        Check whether the client's underlying http client is connected
+208        """
+209        return await self.http_client().is_connected()
+210
+211    async def disconnect(self) -> None:
+212        """
+213        Disconnect both the underlying http and ws clients and
+214        unsubscribe from all subscriptions
+215        """
+216        await self.http_client().provider.disconnect()
+217        await self.ws_client().subscription_manager.unsubscribe_all()
+218        await self.ws_client().provider.disconnect()
+219
+220    def get_account_address(self) -> ChecksumAddress:
+221        """
+222        Get the address associated with the private key that this client
+223        was created with
+224        """
+225        return self.account.address
+226
+227    async def get_storage_value(self, entity_key: EntityKey) -> bytes:
+228        """
+229        Get the storage value stored in the given entity
+230        """
+231        return base64.b64decode(
+232            await self.http_client().eth.get_storage_value(entity_key.as_hex_string())
+233        )
+234
+235    async def get_entity_metadata(self, entity_key: EntityKey) -> EntityMetadata:
+236        """
+237        Get the metadata of the given entity
+238        """
+239        metadata = await self.http_client().eth.get_entity_metadata(
+240            entity_key.as_hex_string()
+241        )
+242
+243        return EntityMetadata(
+244            entity_key=entity_key,
+245            owner=Address(GenericBytes.from_hex_string(metadata.owner)),
+246            expires_at_block=metadata.expiresAtBlock,
+247            string_annotations=list(
+248                map(
+249                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
+250                    metadata.stringAnnotations,
+251                )
+252            ),
+253            numeric_annotations=list(
+254                map(
+255                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
+256                    metadata.numericAnnotations,
+257                )
+258            ),
+259        )
+260
+261    async def get_entities_to_expire_at_block(
+262        self, block_number: int
+263    ) -> Sequence[EntityKey]:
+264        """
+265        Get all entities that will expire at the given block
+266        """
+267        return list(
+268            map(
+269                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
+270                await self.http_client().eth.get_entities_to_expire_at_block(
+271                    block_number
+272                ),
+273            )
+274        )
+275
+276    async def get_entity_count(self) -> int:
+277        """
+278        Get the total entity count in Golem Base
+279        """
+280        return await self.http_client().eth.get_entity_count()
+281
+282    async def get_all_entity_keys(self) -> Sequence[EntityKey]:
+283        """
+284        Get all entity keys in Golem Base
+285        """
+286        return list(
+287            map(
+288                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
+289                await self.http_client().eth.get_all_entity_keys(),
+290            )
+291        )
+292
+293    async def get_entities_of_owner(
+294        self, owner: ChecksumAddress
+295    ) -> Sequence[EntityKey]:
+296        """
+297        Get all the entities owned by the given address
+298        """
+299        return list(
+300            map(
+301                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
+302                # https://github.com/pylint-dev/pylint/issues/3162
+303                # pylint: disable=no-member
+304                await self.http_client().eth.get_entities_of_owner(owner),
+305            )
+306        )
+307
+308    async def query_entities(self, query: str) -> Sequence[QueryEntitiesResult]:
+309        """
+310        Get all entities that satisfy the given Golem Base query
+311        """
+312        return list(
+313            map(
+314                lambda result: QueryEntitiesResult(
+315                    entity_key=result.key, storage_value=base64.b64decode(result.value)
+316                ),
+317                await self.http_client().eth.query_entities(query),
+318            )
+319        )
+320
+321    async def create_entities(
+322        self,
+323        creates: Sequence[GolemBaseCreate],
+324    ) -> Sequence[CreateEntityReturnType]:
+325        """
+326        Create entities in Golem Base
+327        """
+328        return (await self.send_transaction(creates=creates)).creates
+329
+330    async def update_entities(
+331        self,
+332        updates: Sequence[GolemBaseUpdate],
+333    ) -> Sequence[UpdateEntityReturnType]:
+334        """
+335        Update entities in Golem Base
+336        """
+337        return (await self.send_transaction(updates=updates)).updates
+338
+339    async def delete_entities(
+340        self,
+341        deletes: Sequence[GolemBaseDelete],
+342    ) -> Sequence[EntityKey]:
+343        """
+344        Delete entities from Golem Base
+345        """
+346        return (await self.send_transaction(deletes=deletes)).deletes
+347
+348    async def extend_entities(
+349        self,
+350        extensions: Sequence[GolemBaseExtend],
+351    ) -> Sequence[ExtendEntityReturnType]:
+352        """
+353        Extend the BTL of entities in Golem Base
+354        """
+355        return (await self.send_transaction(extensions=extensions)).extensions
+356
+357    async def send_transaction(
+358        self,
+359        creates: Optional[Sequence[GolemBaseCreate]] = None,
+360        updates: Optional[Sequence[GolemBaseUpdate]] = None,
+361        deletes: Optional[Sequence[GolemBaseDelete]] = None,
+362        extensions: Optional[Sequence[GolemBaseExtend]] = None,
+363    ) -> GolemBaseTransactionReceipt:
+364        """
+365        Send a generic transaction to Golem Base.
+366        This transaction can contain multiple create, update, delete and
+367        extend operations
+368        """
+369        tx = GolemBaseTransaction(
+370            creates,
+371            updates,
+372            deletes,
+373            extensions,
+374        )
+375        return await self._send_gb_transaction(tx)
+376
+377    async def _process_golem_base_log_receipt(
+378        self,
+379        log_receipt: LogReceipt,
+380    ) -> GolemBaseTransactionReceipt:
+381        # Read the first entry of the topics array,
+382        # which is the hash of the event signature, identifying the event
+383        topic = AsyncWeb3.to_hex(log_receipt["topics"][0])
+384        # Look up the corresponding event
+385        # If there is no such event in the ABI, it probably needs to be added
+386        event = self.golem_base_contract.get_event_by_topic(topic)
+387        # Use the event to process the whole log
+388        event_data = event.process_log(log_receipt)
+389
+390        creates: List[CreateEntityReturnType] = []
+391        updates: List[UpdateEntityReturnType] = []
+392        deletes: List[EntityKey] = []
+393        extensions: List[ExtendEntityReturnType] = []
+394
+395        match event_data["event"]:
+396            case "GolemBaseStorageEntityCreated":
+397                creates.append(
+398                    CreateEntityReturnType(
+399                        expiration_block=event_data["args"]["expirationBlock"],
+400                        entity_key=EntityKey(
+401                            GenericBytes(
+402                                event_data["args"]["entityKey"].to_bytes(32, "big")
+403                            )
+404                        ),
+405                    )
+406                )
+407            case "GolemBaseStorageEntityUpdated":
+408                updates.append(
+409                    UpdateEntityReturnType(
+410                        expiration_block=event_data["args"]["expirationBlock"],
+411                        entity_key=EntityKey(
+412                            GenericBytes(
+413                                event_data["args"]["entityKey"].to_bytes(32, "big")
+414                            )
+415                        ),
+416                    )
+417                )
+418            case "GolemBaseStorageEntityDeleted":
+419                deletes.append(
+420                    EntityKey(
+421                        GenericBytes(
+422                            event_data["args"]["entityKey"].to_bytes(32, "big")
+423                        ),
+424                    )
+425                )
+426            case "GolemBaseStorageEntityBTLExtended":
+427                extensions.append(
+428                    ExtendEntityReturnType(
+429                        old_expiration_block=event_data["args"]["oldExpirationBlock"],
+430                        new_expiration_block=event_data["args"]["newExpirationBlock"],
+431                        entity_key=EntityKey(
+432                            GenericBytes(
+433                                event_data["args"]["entityKey"].to_bytes(32, "big")
+434                            )
+435                        ),
+436                    )
+437                )
+438            # This is only here for backwards compatibility and can be removed
+439            # once we undeploy kaolin.
+440            case (
+441                "GolemBaseStorageEntityBTLExptended"
+442                | "GolemBaseStorageEntityTTLExptended"
+443            ):
+444                extensions.append(parse_legacy_btl_extended_log(log_receipt))
+445            case other:
+446                raise ValueError(f"Unknown event type: {other}")
+447
+448        return GolemBaseTransactionReceipt(
+449            creates=creates,
+450            updates=updates,
+451            deletes=deletes,
+452            extensions=extensions,
+453        )
+454
+455    async def _process_golem_base_receipt(
+456        self, receipt: TxReceipt
+457    ) -> GolemBaseTransactionReceipt:
+458        # There doesn't seem to be a method for this in the web3 lib.
+459        # The only option in the lib is to iterate over the events in the ABI
+460        # and call process_receipt on each of them to try and decode the logs.
+461        # This is inefficient though compared to reading the actual topic signature
+462        # and immediately selecting the right event from the ABI, which is what
+463        # we do here.
+464        async def process_receipt(
+465            receipt: TxReceipt,
+466        ) -> AsyncGenerator[GolemBaseTransactionReceipt, None]:
+467            for log in receipt["logs"]:
+468                yield await self._process_golem_base_log_receipt(log)
+469
+470        creates: List[CreateEntityReturnType] = []
+471        updates: List[UpdateEntityReturnType] = []
+472        deletes: List[EntityKey] = []
+473        extensions: List[ExtendEntityReturnType] = []
+474
+475        async for res in process_receipt(receipt):
+476            creates.extend(res.creates)
+477            updates.extend(res.updates)
+478            deletes.extend(res.deletes)
+479            extensions.extend(res.extensions)
+480
+481        return GolemBaseTransactionReceipt(
+482            creates=creates,
+483            updates=updates,
+484            deletes=deletes,
+485            extensions=extensions,
+486        )
+487
+488    async def _send_gb_transaction(
+489        self, tx: GolemBaseTransaction
+490    ) -> GolemBaseTransactionReceipt:
+491        txhash = await self.http_client().eth.send_transaction(
+492            {
+493                # https://github.com/pylint-dev/pylint/issues/3162
+494                # pylint: disable=no-member
+495                "to": STORAGE_ADDRESS.as_address(),
+496                "value": AsyncWeb3.to_wei(0, "ether"),
+497                "data": rlp_encode_transaction(tx),
+498            }
+499        )
+500        receipt = await self.http_client().eth.wait_for_transaction_receipt(txhash)
+501        return await self._process_golem_base_receipt(receipt)
+502
+503    async def watch_logs(
+504        self,
+505        create_callback: Callable[[CreateEntityReturnType], None],
+506        update_callback: Callable[[UpdateEntityReturnType], None],
+507        delete_callback: Callable[[EntityKey], None],
+508        extend_callback: Callable[[ExtendEntityReturnType], None],
+509    ) -> None:
+510        """
+511        Subscribe to events on Golem Base.
+512        You can pass in four different callbacks, and the right one will
+513        be invoked for every create, update, delete, and extend operation.
+514        """
+515
+516        async def log_handler(
+517            handler_context: LogsSubscriptionContext,
+518        ) -> None:
+519            # We only use this handler for log receipts
+520            # TypeDicts cannot be checked at runtime
+521            log_receipt = typing.cast(LogReceipt, handler_context.result)
+522            logger.debug("New log: %s", log_receipt)
+523            res = await self._process_golem_base_log_receipt(log_receipt)
+524
+525            for create in res.creates:
+526                create_callback(create)
+527            for update in res.updates:
+528                update_callback(update)
+529            for key in res.deletes:
+530                delete_callback(key)
+531            for extension in res.extensions:
+532                extend_callback(extension)
+533
+534        def create_subscription(topic: HexStr) -> LogsSubscription:
+535            return LogsSubscription(
+536                label=f"Golem Base subscription to topic {topic}",
+537                address=self.golem_base_contract.address,
+538                topics=[topic],
+539                handler=log_handler,
+540                # optional `handler_context` args to help parse a response
+541                handler_context={},
+542            )
+543
+544        async def handle_subscriptions() -> None:
+545            await self._ws_client.subscription_manager.subscribe(
+546                list(
+547                    map(
+548                        lambda event: create_subscription(event.topic),
+549                        self.golem_base_contract.all_events(),
+550                    )
+551                ),
+552            )
+553            # handle subscriptions via configured handlers:
+554            await self.ws_client().subscription_manager.handle_subscriptions()
+555
+556        # Create a long running task to handle subscriptions that we can run on
+557        # the asyncio event loop
+558        task = asyncio.create_task(handle_subscriptions())
+559        self._background_tasks.add(task)
+560
+561        def task_done(task: asyncio.Task) -> None:
+562            logger.info("Subscription background task done, removing...")
+563            self._background_tasks.discard(task)
+564
+565        task.add_done_callback(task_done)
 
@@ -709,509 +706,509 @@

-
 67class GolemBaseClient:
- 68    """
- 69    The Golem Base client used to interact with Golem Base.
- 70    Many useful methods are implemented directly on this type, while more
- 71    generic ethereum methods can be accessed through the underlying
- 72    web3 client that you can access with the
- 73    `GolemBaseClient.http_client()`
- 74    method.
- 75    """
- 76
- 77    _http_client: AsyncWeb3
- 78    _ws_client: AsyncWeb3
- 79    _golem_base_contract: AsyncContract
- 80    _background_tasks: Set[asyncio.Task]
- 81
- 82    @staticmethod
- 83    async def create(
- 84        rpc_url: str, ws_url: str, private_key: Sequence[bytes]
- 85    ) -> "GolemBaseClient":
- 86        """
- 87        Static method to create a `GolemBaseClient` instance,
- 88        this is the preferred method to create an instance.
- 89        """
- 90        ws_client = await AsyncWeb3(WebSocketProvider(ws_url))
- 91        return GolemBaseClient(rpc_url, ws_client, private_key)
- 92
- 93    def __init__(
- 94        self, rpc_url: str, ws_client: AsyncWeb3, private_key: Sequence[bytes]
- 95    ) -> None:
- 96        self._http_client = GolemBaseClient._create_client(rpc_url)
- 97        self._ws_client = ws_client
- 98
- 99        # Keep references to async tasks we created
-100        self._background_tasks = set()
-101
-102        def is_connected(client) -> Callable[[bool], Awaitable[bool]]:
-103            async def inner(show_traceback: bool) -> bool:
-104                try:
-105                    logger.debug("Calling eth_blockNumber to test connectivity...")
-106                    await client.eth.get_block_number()
-107                    return True
-108                except (OSError, ProviderConnectionError) as e:
-109                    logger.debug(
-110                        "Problem connecting to provider", exc_info=show_traceback
-111                    )
-112                    if show_traceback:
-113                        raise ProviderConnectionError(
-114                            "Problem connecting to provider"
-115                        ) from e
-116                    return False
-117
-118            return inner
-119
-120        # The default is_connected method calls web3_clientVersion, but the web3
-121        # API is not enabled on all our nodes, so let's monkey patch this to call
-122        # eth_getBlockNumber instead.
-123        # The method on the provider is usually not called directly, instead you
-124        # can call the eponymous method on the client, which will delegate to the
-125        # provider.
-126        setattr(
-127            self.http_client().provider,
-128            "is_connected",
-129            is_connected(self.http_client()),
-130        )
-131
-132        # Allow caching of certain methods to improve performance
-133        self.http_client().provider.cache_allowed_requests = True
-134
-135        # Set up the ethereum account
-136        self.account = self.http_client().eth.account.from_key(private_key)
-137        # Inject a middleware that will sign transactions with the account that we created
-138        self.http_client().middleware_onion.inject(
-139            # pylint doesn't detect nested @curry annotations properly...
-140            # pylint: disable=no-value-for-parameter
-141            SignAndSendRawMiddlewareBuilder.build(self.account),
-142            layer=0,
-143        )
-144        # Set the account as the default, so we don't need to specify the from field
-145        # every time
-146        self.http_client().eth.default_account = self.account.address
-147        logger.debug("Using account: %s", self.account.address)
-148
-149        # https://github.com/pylint-dev/pylint/issues/3162
-150        # pylint: disable=no-member
-151        self.golem_base_contract = self.http_client().eth.contract(
-152            address=STORAGE_ADDRESS.as_address(),
-153            abi=GOLEM_BASE_ABI,
-154        )
-155        for event in self.golem_base_contract.all_events():
-156            logger.debug(
-157                "Registered event %s with hash %s", event.signature, event.topic
-158            )
-159
-160    @staticmethod
-161    def _create_client(rpc_url: str) -> AsyncWeb3:
-162        client = AsyncWeb3(
-163            AsyncWeb3.AsyncHTTPProvider(rpc_url, request_kwargs={"timeout": 60}),
-164        )
-165        client.eth.attach_methods(
-166            {
-167                "get_storage_value": Method(
-168                    json_rpc_method=RPCEndpoint("golembase_getStorageValue"),
-169                    mungers=[default_root_munger],
-170                ),
-171                "get_entity_metadata": Method(
-172                    json_rpc_method=RPCEndpoint("golembase_getEntityMetaData"),
-173                    mungers=[default_root_munger],
-174                ),
-175                "get_entities_to_expire_at_block": Method(
-176                    json_rpc_method=RPCEndpoint("golembase_getEntitiesToExpireAtBlock"),
-177                    mungers=[default_root_munger],
-178                ),
-179                "get_entity_count": Method(
-180                    json_rpc_method=RPCEndpoint("golembase_getEntityCount"),
-181                    mungers=[default_root_munger],
-182                ),
-183                "get_all_entity_keys": Method(
-184                    json_rpc_method=RPCEndpoint("golembase_getAllEntityKeys"),
-185                    mungers=[default_root_munger],
-186                ),
-187                "get_entities_of_owner": Method(
-188                    json_rpc_method=RPCEndpoint("golembase_getEntitiesOfOwner"),
-189                    mungers=[default_root_munger],
-190                ),
-191                "query_entities": Method(
-192                    json_rpc_method=RPCEndpoint("golembase_queryEntities"),
-193                    mungers=[default_root_munger],
-194                ),
-195            }
-196        )
-197        return client
-198
-199    def http_client(self):
-200        """
-201        Get the underlying web3 http client
-202        """
-203        return self._http_client
-204
-205    def ws_client(self) -> AsyncWeb3:
-206        """
-207        Get the underlying web3 websocket client
-208        """
-209        return self._ws_client
-210
-211    async def is_connected(self) -> bool:
-212        """
-213        Check whether the client's underlying http client is connected
-214        """
-215        return await self.http_client().is_connected()
-216
-217    async def disconnect(self) -> None:
-218        """
-219        Disconnect both the underlying http and ws clients and
-220        unsubscribe from all subscriptions
-221        """
-222        await self.http_client().provider.disconnect()
-223        await self.ws_client().subscription_manager.unsubscribe_all()
-224        await self.ws_client().provider.disconnect()
-225
-226    def get_account_address(self) -> ChecksumAddress:
-227        """
-228        Get the address associated with the private key that this client
-229        was created with
-230        """
-231        return self.account.address
-232
-233    async def get_storage_value(self, entity_key: EntityKey) -> bytes:
-234        """
-235        Get the storage value stored in the given entity
-236        """
-237        return base64.b64decode(
-238            await self.http_client().eth.get_storage_value(entity_key.as_hex_string())
-239        )
-240
-241    async def get_entity_metadata(self, entity_key: EntityKey) -> EntityMetadata:
-242        """
-243        Get the metadata of the given entity
-244        """
-245        metadata = await self.http_client().eth.get_entity_metadata(
-246            entity_key.as_hex_string()
-247        )
-248
-249        return EntityMetadata(
-250            entity_key=entity_key,
-251            owner=Address(GenericBytes.from_hex_string(metadata.owner)),
-252            expires_at_block=metadata.expiresAtBlock,
-253            string_annotations=list(
-254                map(
-255                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
-256                    metadata.stringAnnotations,
-257                )
-258            ),
-259            numeric_annotations=list(
-260                map(
-261                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
-262                    metadata.numericAnnotations,
-263                )
-264            ),
-265        )
-266
-267    async def get_entities_to_expire_at_block(
-268        self, block_number: int
-269    ) -> Iterable[EntityKey]:
-270        """
-271        Get all entities that will expire at the given block
-272        """
-273        return list(
-274            map(
-275                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
-276                await self.http_client().eth.get_entities_to_expire_at_block(
-277                    block_number
-278                ),
-279            )
-280        )
-281
-282    async def get_entity_count(self) -> int:
-283        """
-284        Get the total entity count in Golem Base
-285        """
-286        return await self.http_client().eth.get_entity_count()
-287
-288    async def get_all_entity_keys(self) -> Sequence[EntityKey]:
-289        """
-290        Get all entity keys in Golem Base
-291        """
-292        return list(
-293            map(
-294                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
-295                await self.http_client().eth.get_all_entity_keys(),
-296            )
-297        )
-298
-299    async def get_entities_of_owner(self, owner: Address) -> Sequence[EntityKey]:
-300        """
-301        Get all the entities owned by the given address
-302        """
-303        return list(
-304            map(
-305                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
-306                # https://github.com/pylint-dev/pylint/issues/3162
-307                # pylint: disable=no-member
-308                await self.http_client().eth.get_entities_of_owner(owner),
-309            )
-310        )
-311
-312    async def query_entities(self, query: str) -> Sequence[QueryEntitiesResult]:
-313        """
-314        Get all entities that satisfy the given Golem Base query
-315        """
-316        return list(
-317            map(
-318                lambda result: QueryEntitiesResult(
-319                    entity_key=result.key, storage_value=base64.b64decode(result.value)
-320                ),
-321                await self.http_client().eth.query_entities(query),
-322            )
-323        )
-324
-325    async def create_entities(
-326        self,
-327        creates: Sequence[GolemBaseCreate],
-328    ) -> Iterable[CreateEntityReturnType]:
-329        """
-330        Create entities in Golem Base
-331        """
-332        return (await self.send_transaction(creates=creates)).creates
-333
-334    async def update_entities(
-335        self,
-336        updates: Sequence[GolemBaseUpdate],
-337    ) -> Iterable[UpdateEntityReturnType]:
-338        """
-339        Update entities in Golem Base
-340        """
-341        return (await self.send_transaction(updates=updates)).updates
-342
-343    async def delete_entities(
-344        self,
-345        deletes: Sequence[GolemBaseDelete],
-346    ) -> Iterable[EntityKey]:
-347        """
-348        Delete entities from Golem Base
-349        """
-350        return (await self.send_transaction(deletes=deletes)).deletes
-351
-352    async def extend_entities(
-353        self,
-354        extensions: Sequence[GolemBaseExtend],
-355    ) -> Iterable[ExtendEntityReturnType]:
-356        """
-357        Extend the BTL of entities in Golem Base
-358        """
-359        return (await self.send_transaction(extensions=extensions)).extensions
-360
-361    async def send_transaction(
-362        self,
-363        creates: Optional[Sequence[GolemBaseCreate]] = None,
-364        updates: Optional[Sequence[GolemBaseUpdate]] = None,
-365        deletes: Optional[Sequence[GolemBaseDelete]] = None,
-366        extensions: Optional[Sequence[GolemBaseExtend]] = None,
-367    ) -> GolemBaseTransactionReceipt:
-368        """
-369        Send a generic transaction to Golem Base.
-370        This transaction can contain multiple create, update, delete and
-371        extend operations
-372        """
-373        tx = GolemBaseTransaction(
-374            creates,
-375            updates,
-376            deletes,
-377            extensions,
-378        )
-379        return await self._send_gb_transaction(tx)
-380
-381    async def _process_golem_base_log_receipt(
-382        self,
-383        log_receipt: LogReceipt,
-384    ) -> GolemBaseTransactionReceipt:
-385        # Read the first entry of the topics array,
-386        # which is the hash of the event signature, identifying the event
-387        topic = AsyncWeb3.to_hex(log_receipt["topics"][0])
-388        # Look up the corresponding event
-389        # If there is no such event in the ABI, it probably needs to be added
-390        event = self.golem_base_contract.get_event_by_topic(topic)
-391        # Use the event to process the whole log
-392        event_data = event.process_log(log_receipt)
-393
-394        creates: List[CreateEntityReturnType] = []
-395        updates: List[UpdateEntityReturnType] = []
-396        deletes: List[EntityKey] = []
-397        extensions: List[ExtendEntityReturnType] = []
-398
-399        match event_data["event"]:
-400            case "GolemBaseStorageEntityCreated":
-401                creates.append(
-402                    CreateEntityReturnType(
-403                        expiration_block=event_data["args"]["expirationBlock"],
-404                        entity_key=EntityKey(
-405                            GenericBytes(
-406                                event_data["args"]["entityKey"].to_bytes(32, "big")
-407                            )
-408                        ),
-409                    )
-410                )
-411            case "GolemBaseStorageEntityUpdated":
-412                updates.append(
-413                    UpdateEntityReturnType(
-414                        expiration_block=event_data["args"]["expirationBlock"],
-415                        entity_key=EntityKey(
-416                            GenericBytes(
-417                                event_data["args"]["entityKey"].to_bytes(32, "big")
-418                            )
-419                        ),
-420                    )
-421                )
-422            case "GolemBaseStorageEntityDeleted":
-423                deletes.append(
-424                    EntityKey(
-425                        GenericBytes(
-426                            event_data["args"]["entityKey"].to_bytes(32, "big")
-427                        ),
-428                    )
-429                )
-430            case "GolemBaseStorageEntityBTLExtended":
-431                extensions.append(
-432                    ExtendEntityReturnType(
-433                        old_expiration_block=event_data["args"]["old_expirationBlock"],
-434                        new_expiration_block=event_data["args"]["new_expirationBlock"],
-435                        entity_key=EntityKey(
-436                            GenericBytes(
-437                                event_data["args"]["entityKey"].to_bytes(32, "big")
-438                            )
-439                        ),
-440                    )
-441                )
-442            # This is only here for backwards compatibility and can be removed
-443            # once we undeploy kaolin.
-444            case (
-445                "GolemBaseStorageEntityBTLExptended"
-446                | "GolemBaseStorageEntityTTLExptended"
-447            ):
-448                extensions.append(parse_legacy_btl_extended_log(log_receipt))
-449            case other:
-450                raise ValueError(f"Unknown event type: {other}")
-451
-452        return GolemBaseTransactionReceipt(
-453            creates=creates,
-454            updates=updates,
-455            deletes=deletes,
-456            extensions=extensions,
-457        )
-458
-459    async def _process_golem_base_receipt(
-460        self, receipt: TxReceipt
-461    ) -> GolemBaseTransactionReceipt:
-462        # There doesn't seem to be a method for this in the web3 lib.
-463        # The only option in the lib is to iterate over the events in the ABI
-464        # and call process_receipt on each of them to try and decode the logs.
-465        # This is inefficient though compared to reading the actual topic signature
-466        # and immediately selecting the right event from the ABI, which is what
-467        # we do here.
-468        async def process_receipt(
-469            receipt: TxReceipt,
-470        ) -> AsyncGenerator[GolemBaseTransactionReceipt, None]:
-471            for log in receipt["logs"]:
-472                yield await self._process_golem_base_log_receipt(log)
-473
-474        creates: List[CreateEntityReturnType] = []
-475        updates: List[UpdateEntityReturnType] = []
-476        deletes: List[EntityKey] = []
-477        extensions: List[ExtendEntityReturnType] = []
-478
-479        async for res in process_receipt(receipt):
-480            creates.extend(res.creates)
-481            updates.extend(res.updates)
-482            deletes.extend(res.deletes)
-483            extensions.extend(res.extensions)
-484
-485        return GolemBaseTransactionReceipt(
-486            creates=creates,
-487            updates=updates,
-488            deletes=deletes,
-489            extensions=extensions,
-490        )
-491
-492    async def _send_gb_transaction(
-493        self, tx: GolemBaseTransaction
-494    ) -> GolemBaseTransactionReceipt:
-495        txhash = await self.http_client().eth.send_transaction(
-496            {
-497                # https://github.com/pylint-dev/pylint/issues/3162
-498                # pylint: disable=no-member
-499                "to": STORAGE_ADDRESS.as_address(),
-500                "value": AsyncWeb3.to_wei(0, "ether"),
-501                "data": rlp_encode_transaction(tx),
-502            }
-503        )
-504        receipt = await self.http_client().eth.wait_for_transaction_receipt(txhash)
-505        return await self._process_golem_base_receipt(receipt)
-506
-507    async def watch_logs(
-508        self,
-509        create_callback: Callable[[CreateEntityReturnType], None],
-510        update_callback: Callable[[UpdateEntityReturnType], None],
-511        delete_callback: Callable[[EntityKey], None],
-512        extend_callback: Callable[[ExtendEntityReturnType], None],
-513    ) -> None:
-514        """
-515        Subscribe to events on Golem Base.
-516        You can pass in four different callbacks, and the right one will
-517        be invoked for every create, update, delete, and extend operation.
-518        """
-519
-520        async def log_handler(
-521            handler_context: LogsSubscriptionContext,
-522        ) -> None:
-523            # We only use this handler for log receipts
-524            # TypeDicts cannot be checked at runtime
-525            log_receipt = typing.cast(LogReceipt, handler_context.result)
-526            logger.debug("New log: %s", log_receipt)
-527            res = await self._process_golem_base_log_receipt(log_receipt)
-528
-529            for create in res.creates:
-530                create_callback(create)
-531            for update in res.updates:
-532                update_callback(update)
-533            for key in res.deletes:
-534                delete_callback(key)
-535            for extension in res.extensions:
-536                extend_callback(extension)
-537
-538        def create_subscription(topic: HexStr) -> LogsSubscription:
-539            return LogsSubscription(
-540                label=f"Golem Base subscription to topic {topic}",
-541                address=self.golem_base_contract.address,
-542                topics=[topic],
-543                handler=log_handler,
-544                # optional `handler_context` args to help parse a response
-545                handler_context={},
-546            )
-547
-548        async def handle_subscriptions() -> None:
-549            await self._ws_client.subscription_manager.subscribe(
-550                list(
-551                    map(
-552                        lambda event: create_subscription(event.topic),
-553                        self.golem_base_contract.all_events(),
-554                    )
-555                ),
-556            )
-557            # handle subscriptions via configured handlers:
-558            await self.ws_client().subscription_manager.handle_subscriptions()
-559
-560        # Create a long running task to handle subscriptions that we can run on
-561        # the asyncio event loop
-562        task = asyncio.create_task(handle_subscriptions())
-563        self._background_tasks.add(task)
-564
-565        def task_done(task: asyncio.Task) -> None:
-566            logger.info("Subscription background task done, removing...")
-567            self._background_tasks.discard(task)
-568
-569        task.add_done_callback(task_done)
+            
 64class GolemBaseClient:
+ 65    """
+ 66    The Golem Base client used to interact with Golem Base.
+ 67    Many useful methods are implemented directly on this type, while more
+ 68    generic ethereum methods can be accessed through the underlying
+ 69    web3 client that you can access with the
+ 70    `GolemBaseClient.http_client()`
+ 71    method.
+ 72    """
+ 73
+ 74    _http_client: AsyncWeb3
+ 75    _ws_client: AsyncWeb3
+ 76    _golem_base_contract: AsyncContract
+ 77    _background_tasks: Set[asyncio.Task]
+ 78
+ 79    @staticmethod
+ 80    async def create(
+ 81        rpc_url: str, ws_url: str, private_key: bytes
+ 82    ) -> "GolemBaseClient":
+ 83        """
+ 84        Static method to create a `GolemBaseClient` instance,
+ 85        this is the preferred method to create an instance.
+ 86        """
+ 87        ws_client = await AsyncWeb3(WebSocketProvider(ws_url))
+ 88        return GolemBaseClient(rpc_url, ws_client, private_key)
+ 89
+ 90    def __init__(self, rpc_url: str, ws_client: AsyncWeb3, private_key: bytes) -> None:
+ 91        self._http_client = GolemBaseClient._create_client(rpc_url)
+ 92        self._ws_client = ws_client
+ 93
+ 94        # Keep references to async tasks we created
+ 95        self._background_tasks = set()
+ 96
+ 97        def is_connected(client) -> Callable[[bool], Awaitable[bool]]:
+ 98            async def inner(show_traceback: bool) -> bool:
+ 99                try:
+100                    logger.debug("Calling eth_blockNumber to test connectivity...")
+101                    await client.eth.get_block_number()
+102                    return True
+103                except (OSError, ProviderConnectionError) as e:
+104                    logger.debug(
+105                        "Problem connecting to provider", exc_info=show_traceback
+106                    )
+107                    if show_traceback:
+108                        raise ProviderConnectionError(
+109                            "Problem connecting to provider"
+110                        ) from e
+111                    return False
+112
+113            return inner
+114
+115        # The default is_connected method calls web3_clientVersion, but the web3
+116        # API is not enabled on all our nodes, so let's monkey patch this to call
+117        # eth_getBlockNumber instead.
+118        # The method on the provider is usually not called directly, instead you
+119        # can call the eponymous method on the client, which will delegate to the
+120        # provider.
+121        setattr(
+122            self.http_client().provider,
+123            "is_connected",
+124            is_connected(self.http_client()),
+125        )
+126
+127        # Allow caching of certain methods to improve performance
+128        self.http_client().provider.cache_allowed_requests = True
+129
+130        # Set up the ethereum account
+131        self.account = self.http_client().eth.account.from_key(private_key)
+132        # Inject a middleware that will sign transactions with the account that we created
+133        self.http_client().middleware_onion.inject(
+134            # pylint doesn't detect nested @curry annotations properly...
+135            # pylint: disable=no-value-for-parameter
+136            SignAndSendRawMiddlewareBuilder.build(self.account),
+137            layer=0,
+138        )
+139        # Set the account as the default, so we don't need to specify the from field
+140        # every time
+141        self.http_client().eth.default_account = self.account.address
+142        logger.debug("Using account: %s", self.account.address)
+143
+144        # https://github.com/pylint-dev/pylint/issues/3162
+145        # pylint: disable=no-member
+146        self.golem_base_contract = self.http_client().eth.contract(
+147            address=STORAGE_ADDRESS.as_address(),
+148            abi=GOLEM_BASE_ABI,
+149        )
+150        for event in self.golem_base_contract.all_events():
+151            logger.debug(
+152                "Registered event %s with hash %s", event.signature, event.topic
+153            )
+154
+155    @staticmethod
+156    def _create_client(rpc_url: str) -> AsyncWeb3:
+157        client = AsyncWeb3(
+158            AsyncWeb3.AsyncHTTPProvider(rpc_url, request_kwargs={"timeout": 60}),
+159        )
+160        client.eth.attach_methods(
+161            {
+162                "get_storage_value": Method(
+163                    json_rpc_method=RPCEndpoint("golembase_getStorageValue"),
+164                    mungers=[default_root_munger],
+165                ),
+166                "get_entity_metadata": Method(
+167                    json_rpc_method=RPCEndpoint("golembase_getEntityMetaData"),
+168                    mungers=[default_root_munger],
+169                ),
+170                "get_entities_to_expire_at_block": Method(
+171                    json_rpc_method=RPCEndpoint("golembase_getEntitiesToExpireAtBlock"),
+172                    mungers=[default_root_munger],
+173                ),
+174                "get_entity_count": Method(
+175                    json_rpc_method=RPCEndpoint("golembase_getEntityCount"),
+176                    mungers=[default_root_munger],
+177                ),
+178                "get_all_entity_keys": Method(
+179                    json_rpc_method=RPCEndpoint("golembase_getAllEntityKeys"),
+180                    mungers=[default_root_munger],
+181                ),
+182                "get_entities_of_owner": Method(
+183                    json_rpc_method=RPCEndpoint("golembase_getEntitiesOfOwner"),
+184                    mungers=[default_root_munger],
+185                ),
+186                "query_entities": Method(
+187                    json_rpc_method=RPCEndpoint("golembase_queryEntities"),
+188                    mungers=[default_root_munger],
+189                ),
+190            }
+191        )
+192        return client
+193
+194    def http_client(self):
+195        """
+196        Get the underlying web3 http client
+197        """
+198        return self._http_client
+199
+200    def ws_client(self) -> AsyncWeb3:
+201        """
+202        Get the underlying web3 websocket client
+203        """
+204        return self._ws_client
+205
+206    async def is_connected(self) -> bool:
+207        """
+208        Check whether the client's underlying http client is connected
+209        """
+210        return await self.http_client().is_connected()
+211
+212    async def disconnect(self) -> None:
+213        """
+214        Disconnect both the underlying http and ws clients and
+215        unsubscribe from all subscriptions
+216        """
+217        await self.http_client().provider.disconnect()
+218        await self.ws_client().subscription_manager.unsubscribe_all()
+219        await self.ws_client().provider.disconnect()
+220
+221    def get_account_address(self) -> ChecksumAddress:
+222        """
+223        Get the address associated with the private key that this client
+224        was created with
+225        """
+226        return self.account.address
+227
+228    async def get_storage_value(self, entity_key: EntityKey) -> bytes:
+229        """
+230        Get the storage value stored in the given entity
+231        """
+232        return base64.b64decode(
+233            await self.http_client().eth.get_storage_value(entity_key.as_hex_string())
+234        )
+235
+236    async def get_entity_metadata(self, entity_key: EntityKey) -> EntityMetadata:
+237        """
+238        Get the metadata of the given entity
+239        """
+240        metadata = await self.http_client().eth.get_entity_metadata(
+241            entity_key.as_hex_string()
+242        )
+243
+244        return EntityMetadata(
+245            entity_key=entity_key,
+246            owner=Address(GenericBytes.from_hex_string(metadata.owner)),
+247            expires_at_block=metadata.expiresAtBlock,
+248            string_annotations=list(
+249                map(
+250                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
+251                    metadata.stringAnnotations,
+252                )
+253            ),
+254            numeric_annotations=list(
+255                map(
+256                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
+257                    metadata.numericAnnotations,
+258                )
+259            ),
+260        )
+261
+262    async def get_entities_to_expire_at_block(
+263        self, block_number: int
+264    ) -> Sequence[EntityKey]:
+265        """
+266        Get all entities that will expire at the given block
+267        """
+268        return list(
+269            map(
+270                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
+271                await self.http_client().eth.get_entities_to_expire_at_block(
+272                    block_number
+273                ),
+274            )
+275        )
+276
+277    async def get_entity_count(self) -> int:
+278        """
+279        Get the total entity count in Golem Base
+280        """
+281        return await self.http_client().eth.get_entity_count()
+282
+283    async def get_all_entity_keys(self) -> Sequence[EntityKey]:
+284        """
+285        Get all entity keys in Golem Base
+286        """
+287        return list(
+288            map(
+289                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
+290                await self.http_client().eth.get_all_entity_keys(),
+291            )
+292        )
+293
+294    async def get_entities_of_owner(
+295        self, owner: ChecksumAddress
+296    ) -> Sequence[EntityKey]:
+297        """
+298        Get all the entities owned by the given address
+299        """
+300        return list(
+301            map(
+302                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
+303                # https://github.com/pylint-dev/pylint/issues/3162
+304                # pylint: disable=no-member
+305                await self.http_client().eth.get_entities_of_owner(owner),
+306            )
+307        )
+308
+309    async def query_entities(self, query: str) -> Sequence[QueryEntitiesResult]:
+310        """
+311        Get all entities that satisfy the given Golem Base query
+312        """
+313        return list(
+314            map(
+315                lambda result: QueryEntitiesResult(
+316                    entity_key=result.key, storage_value=base64.b64decode(result.value)
+317                ),
+318                await self.http_client().eth.query_entities(query),
+319            )
+320        )
+321
+322    async def create_entities(
+323        self,
+324        creates: Sequence[GolemBaseCreate],
+325    ) -> Sequence[CreateEntityReturnType]:
+326        """
+327        Create entities in Golem Base
+328        """
+329        return (await self.send_transaction(creates=creates)).creates
+330
+331    async def update_entities(
+332        self,
+333        updates: Sequence[GolemBaseUpdate],
+334    ) -> Sequence[UpdateEntityReturnType]:
+335        """
+336        Update entities in Golem Base
+337        """
+338        return (await self.send_transaction(updates=updates)).updates
+339
+340    async def delete_entities(
+341        self,
+342        deletes: Sequence[GolemBaseDelete],
+343    ) -> Sequence[EntityKey]:
+344        """
+345        Delete entities from Golem Base
+346        """
+347        return (await self.send_transaction(deletes=deletes)).deletes
+348
+349    async def extend_entities(
+350        self,
+351        extensions: Sequence[GolemBaseExtend],
+352    ) -> Sequence[ExtendEntityReturnType]:
+353        """
+354        Extend the BTL of entities in Golem Base
+355        """
+356        return (await self.send_transaction(extensions=extensions)).extensions
+357
+358    async def send_transaction(
+359        self,
+360        creates: Optional[Sequence[GolemBaseCreate]] = None,
+361        updates: Optional[Sequence[GolemBaseUpdate]] = None,
+362        deletes: Optional[Sequence[GolemBaseDelete]] = None,
+363        extensions: Optional[Sequence[GolemBaseExtend]] = None,
+364    ) -> GolemBaseTransactionReceipt:
+365        """
+366        Send a generic transaction to Golem Base.
+367        This transaction can contain multiple create, update, delete and
+368        extend operations
+369        """
+370        tx = GolemBaseTransaction(
+371            creates,
+372            updates,
+373            deletes,
+374            extensions,
+375        )
+376        return await self._send_gb_transaction(tx)
+377
+378    async def _process_golem_base_log_receipt(
+379        self,
+380        log_receipt: LogReceipt,
+381    ) -> GolemBaseTransactionReceipt:
+382        # Read the first entry of the topics array,
+383        # which is the hash of the event signature, identifying the event
+384        topic = AsyncWeb3.to_hex(log_receipt["topics"][0])
+385        # Look up the corresponding event
+386        # If there is no such event in the ABI, it probably needs to be added
+387        event = self.golem_base_contract.get_event_by_topic(topic)
+388        # Use the event to process the whole log
+389        event_data = event.process_log(log_receipt)
+390
+391        creates: List[CreateEntityReturnType] = []
+392        updates: List[UpdateEntityReturnType] = []
+393        deletes: List[EntityKey] = []
+394        extensions: List[ExtendEntityReturnType] = []
+395
+396        match event_data["event"]:
+397            case "GolemBaseStorageEntityCreated":
+398                creates.append(
+399                    CreateEntityReturnType(
+400                        expiration_block=event_data["args"]["expirationBlock"],
+401                        entity_key=EntityKey(
+402                            GenericBytes(
+403                                event_data["args"]["entityKey"].to_bytes(32, "big")
+404                            )
+405                        ),
+406                    )
+407                )
+408            case "GolemBaseStorageEntityUpdated":
+409                updates.append(
+410                    UpdateEntityReturnType(
+411                        expiration_block=event_data["args"]["expirationBlock"],
+412                        entity_key=EntityKey(
+413                            GenericBytes(
+414                                event_data["args"]["entityKey"].to_bytes(32, "big")
+415                            )
+416                        ),
+417                    )
+418                )
+419            case "GolemBaseStorageEntityDeleted":
+420                deletes.append(
+421                    EntityKey(
+422                        GenericBytes(
+423                            event_data["args"]["entityKey"].to_bytes(32, "big")
+424                        ),
+425                    )
+426                )
+427            case "GolemBaseStorageEntityBTLExtended":
+428                extensions.append(
+429                    ExtendEntityReturnType(
+430                        old_expiration_block=event_data["args"]["oldExpirationBlock"],
+431                        new_expiration_block=event_data["args"]["newExpirationBlock"],
+432                        entity_key=EntityKey(
+433                            GenericBytes(
+434                                event_data["args"]["entityKey"].to_bytes(32, "big")
+435                            )
+436                        ),
+437                    )
+438                )
+439            # This is only here for backwards compatibility and can be removed
+440            # once we undeploy kaolin.
+441            case (
+442                "GolemBaseStorageEntityBTLExptended"
+443                | "GolemBaseStorageEntityTTLExptended"
+444            ):
+445                extensions.append(parse_legacy_btl_extended_log(log_receipt))
+446            case other:
+447                raise ValueError(f"Unknown event type: {other}")
+448
+449        return GolemBaseTransactionReceipt(
+450            creates=creates,
+451            updates=updates,
+452            deletes=deletes,
+453            extensions=extensions,
+454        )
+455
+456    async def _process_golem_base_receipt(
+457        self, receipt: TxReceipt
+458    ) -> GolemBaseTransactionReceipt:
+459        # There doesn't seem to be a method for this in the web3 lib.
+460        # The only option in the lib is to iterate over the events in the ABI
+461        # and call process_receipt on each of them to try and decode the logs.
+462        # This is inefficient though compared to reading the actual topic signature
+463        # and immediately selecting the right event from the ABI, which is what
+464        # we do here.
+465        async def process_receipt(
+466            receipt: TxReceipt,
+467        ) -> AsyncGenerator[GolemBaseTransactionReceipt, None]:
+468            for log in receipt["logs"]:
+469                yield await self._process_golem_base_log_receipt(log)
+470
+471        creates: List[CreateEntityReturnType] = []
+472        updates: List[UpdateEntityReturnType] = []
+473        deletes: List[EntityKey] = []
+474        extensions: List[ExtendEntityReturnType] = []
+475
+476        async for res in process_receipt(receipt):
+477            creates.extend(res.creates)
+478            updates.extend(res.updates)
+479            deletes.extend(res.deletes)
+480            extensions.extend(res.extensions)
+481
+482        return GolemBaseTransactionReceipt(
+483            creates=creates,
+484            updates=updates,
+485            deletes=deletes,
+486            extensions=extensions,
+487        )
+488
+489    async def _send_gb_transaction(
+490        self, tx: GolemBaseTransaction
+491    ) -> GolemBaseTransactionReceipt:
+492        txhash = await self.http_client().eth.send_transaction(
+493            {
+494                # https://github.com/pylint-dev/pylint/issues/3162
+495                # pylint: disable=no-member
+496                "to": STORAGE_ADDRESS.as_address(),
+497                "value": AsyncWeb3.to_wei(0, "ether"),
+498                "data": rlp_encode_transaction(tx),
+499            }
+500        )
+501        receipt = await self.http_client().eth.wait_for_transaction_receipt(txhash)
+502        return await self._process_golem_base_receipt(receipt)
+503
+504    async def watch_logs(
+505        self,
+506        create_callback: Callable[[CreateEntityReturnType], None],
+507        update_callback: Callable[[UpdateEntityReturnType], None],
+508        delete_callback: Callable[[EntityKey], None],
+509        extend_callback: Callable[[ExtendEntityReturnType], None],
+510    ) -> None:
+511        """
+512        Subscribe to events on Golem Base.
+513        You can pass in four different callbacks, and the right one will
+514        be invoked for every create, update, delete, and extend operation.
+515        """
+516
+517        async def log_handler(
+518            handler_context: LogsSubscriptionContext,
+519        ) -> None:
+520            # We only use this handler for log receipts
+521            # TypeDicts cannot be checked at runtime
+522            log_receipt = typing.cast(LogReceipt, handler_context.result)
+523            logger.debug("New log: %s", log_receipt)
+524            res = await self._process_golem_base_log_receipt(log_receipt)
+525
+526            for create in res.creates:
+527                create_callback(create)
+528            for update in res.updates:
+529                update_callback(update)
+530            for key in res.deletes:
+531                delete_callback(key)
+532            for extension in res.extensions:
+533                extend_callback(extension)
+534
+535        def create_subscription(topic: HexStr) -> LogsSubscription:
+536            return LogsSubscription(
+537                label=f"Golem Base subscription to topic {topic}",
+538                address=self.golem_base_contract.address,
+539                topics=[topic],
+540                handler=log_handler,
+541                # optional `handler_context` args to help parse a response
+542                handler_context={},
+543            )
+544
+545        async def handle_subscriptions() -> None:
+546            await self._ws_client.subscription_manager.subscribe(
+547                list(
+548                    map(
+549                        lambda event: create_subscription(event.topic),
+550                        self.golem_base_contract.all_events(),
+551                    )
+552                ),
+553            )
+554            # handle subscriptions via configured handlers:
+555            await self.ws_client().subscription_manager.handle_subscriptions()
+556
+557        # Create a long running task to handle subscriptions that we can run on
+558        # the asyncio event loop
+559        task = asyncio.create_task(handle_subscriptions())
+560        self._background_tasks.add(task)
+561
+562        def task_done(task: asyncio.Task) -> None:
+563            logger.info("Subscription background task done, removing...")
+564            self._background_tasks.discard(task)
+565
+566        task.add_done_callback(task_done)
 
@@ -1228,78 +1225,76 @@

- GolemBaseClient( rpc_url: str, ws_client: web3.main.AsyncWeb3, private_key: Sequence[bytes]) + GolemBaseClient(rpc_url: str, ws_client: web3.main.AsyncWeb3, private_key: bytes)
-
 93    def __init__(
- 94        self, rpc_url: str, ws_client: AsyncWeb3, private_key: Sequence[bytes]
- 95    ) -> None:
- 96        self._http_client = GolemBaseClient._create_client(rpc_url)
- 97        self._ws_client = ws_client
- 98
- 99        # Keep references to async tasks we created
-100        self._background_tasks = set()
-101
-102        def is_connected(client) -> Callable[[bool], Awaitable[bool]]:
-103            async def inner(show_traceback: bool) -> bool:
-104                try:
-105                    logger.debug("Calling eth_blockNumber to test connectivity...")
-106                    await client.eth.get_block_number()
-107                    return True
-108                except (OSError, ProviderConnectionError) as e:
-109                    logger.debug(
-110                        "Problem connecting to provider", exc_info=show_traceback
-111                    )
-112                    if show_traceback:
-113                        raise ProviderConnectionError(
-114                            "Problem connecting to provider"
-115                        ) from e
-116                    return False
-117
-118            return inner
-119
-120        # The default is_connected method calls web3_clientVersion, but the web3
-121        # API is not enabled on all our nodes, so let's monkey patch this to call
-122        # eth_getBlockNumber instead.
-123        # The method on the provider is usually not called directly, instead you
-124        # can call the eponymous method on the client, which will delegate to the
-125        # provider.
-126        setattr(
-127            self.http_client().provider,
-128            "is_connected",
-129            is_connected(self.http_client()),
-130        )
-131
-132        # Allow caching of certain methods to improve performance
-133        self.http_client().provider.cache_allowed_requests = True
-134
-135        # Set up the ethereum account
-136        self.account = self.http_client().eth.account.from_key(private_key)
-137        # Inject a middleware that will sign transactions with the account that we created
-138        self.http_client().middleware_onion.inject(
-139            # pylint doesn't detect nested @curry annotations properly...
-140            # pylint: disable=no-value-for-parameter
-141            SignAndSendRawMiddlewareBuilder.build(self.account),
-142            layer=0,
-143        )
-144        # Set the account as the default, so we don't need to specify the from field
-145        # every time
-146        self.http_client().eth.default_account = self.account.address
-147        logger.debug("Using account: %s", self.account.address)
-148
-149        # https://github.com/pylint-dev/pylint/issues/3162
-150        # pylint: disable=no-member
-151        self.golem_base_contract = self.http_client().eth.contract(
-152            address=STORAGE_ADDRESS.as_address(),
-153            abi=GOLEM_BASE_ABI,
-154        )
-155        for event in self.golem_base_contract.all_events():
-156            logger.debug(
-157                "Registered event %s with hash %s", event.signature, event.topic
-158            )
+            
 90    def __init__(self, rpc_url: str, ws_client: AsyncWeb3, private_key: bytes) -> None:
+ 91        self._http_client = GolemBaseClient._create_client(rpc_url)
+ 92        self._ws_client = ws_client
+ 93
+ 94        # Keep references to async tasks we created
+ 95        self._background_tasks = set()
+ 96
+ 97        def is_connected(client) -> Callable[[bool], Awaitable[bool]]:
+ 98            async def inner(show_traceback: bool) -> bool:
+ 99                try:
+100                    logger.debug("Calling eth_blockNumber to test connectivity...")
+101                    await client.eth.get_block_number()
+102                    return True
+103                except (OSError, ProviderConnectionError) as e:
+104                    logger.debug(
+105                        "Problem connecting to provider", exc_info=show_traceback
+106                    )
+107                    if show_traceback:
+108                        raise ProviderConnectionError(
+109                            "Problem connecting to provider"
+110                        ) from e
+111                    return False
+112
+113            return inner
+114
+115        # The default is_connected method calls web3_clientVersion, but the web3
+116        # API is not enabled on all our nodes, so let's monkey patch this to call
+117        # eth_getBlockNumber instead.
+118        # The method on the provider is usually not called directly, instead you
+119        # can call the eponymous method on the client, which will delegate to the
+120        # provider.
+121        setattr(
+122            self.http_client().provider,
+123            "is_connected",
+124            is_connected(self.http_client()),
+125        )
+126
+127        # Allow caching of certain methods to improve performance
+128        self.http_client().provider.cache_allowed_requests = True
+129
+130        # Set up the ethereum account
+131        self.account = self.http_client().eth.account.from_key(private_key)
+132        # Inject a middleware that will sign transactions with the account that we created
+133        self.http_client().middleware_onion.inject(
+134            # pylint doesn't detect nested @curry annotations properly...
+135            # pylint: disable=no-value-for-parameter
+136            SignAndSendRawMiddlewareBuilder.build(self.account),
+137            layer=0,
+138        )
+139        # Set the account as the default, so we don't need to specify the from field
+140        # every time
+141        self.http_client().eth.default_account = self.account.address
+142        logger.debug("Using account: %s", self.account.address)
+143
+144        # https://github.com/pylint-dev/pylint/issues/3162
+145        # pylint: disable=no-member
+146        self.golem_base_contract = self.http_client().eth.contract(
+147            address=STORAGE_ADDRESS.as_address(),
+148            abi=GOLEM_BASE_ABI,
+149        )
+150        for event in self.golem_base_contract.all_events():
+151            logger.debug(
+152                "Registered event %s with hash %s", event.signature, event.topic
+153            )
 
@@ -1312,22 +1307,22 @@

@staticmethod
async def - create( rpc_url: str, ws_url: str, private_key: Sequence[bytes]) -> GolemBaseClient: + create( rpc_url: str, ws_url: str, private_key: bytes) -> GolemBaseClient:

-
82    @staticmethod
-83    async def create(
-84        rpc_url: str, ws_url: str, private_key: Sequence[bytes]
-85    ) -> "GolemBaseClient":
-86        """
-87        Static method to create a `GolemBaseClient` instance,
-88        this is the preferred method to create an instance.
-89        """
-90        ws_client = await AsyncWeb3(WebSocketProvider(ws_url))
-91        return GolemBaseClient(rpc_url, ws_client, private_key)
+            
79    @staticmethod
+80    async def create(
+81        rpc_url: str, ws_url: str, private_key: bytes
+82    ) -> "GolemBaseClient":
+83        """
+84        Static method to create a `GolemBaseClient` instance,
+85        this is the preferred method to create an instance.
+86        """
+87        ws_client = await AsyncWeb3(WebSocketProvider(ws_url))
+88        return GolemBaseClient(rpc_url, ws_client, private_key)
 
@@ -1370,11 +1365,11 @@

-
199    def http_client(self):
-200        """
-201        Get the underlying web3 http client
-202        """
-203        return self._http_client
+            
194    def http_client(self):
+195        """
+196        Get the underlying web3 http client
+197        """
+198        return self._http_client
 
@@ -1394,11 +1389,11 @@

-
205    def ws_client(self) -> AsyncWeb3:
-206        """
-207        Get the underlying web3 websocket client
-208        """
-209        return self._ws_client
+            
200    def ws_client(self) -> AsyncWeb3:
+201        """
+202        Get the underlying web3 websocket client
+203        """
+204        return self._ws_client
 
@@ -1418,11 +1413,11 @@

-
211    async def is_connected(self) -> bool:
-212        """
-213        Check whether the client's underlying http client is connected
-214        """
-215        return await self.http_client().is_connected()
+            
206    async def is_connected(self) -> bool:
+207        """
+208        Check whether the client's underlying http client is connected
+209        """
+210        return await self.http_client().is_connected()
 
@@ -1442,14 +1437,14 @@

-
217    async def disconnect(self) -> None:
-218        """
-219        Disconnect both the underlying http and ws clients and
-220        unsubscribe from all subscriptions
-221        """
-222        await self.http_client().provider.disconnect()
-223        await self.ws_client().subscription_manager.unsubscribe_all()
-224        await self.ws_client().provider.disconnect()
+            
212    async def disconnect(self) -> None:
+213        """
+214        Disconnect both the underlying http and ws clients and
+215        unsubscribe from all subscriptions
+216        """
+217        await self.http_client().provider.disconnect()
+218        await self.ws_client().subscription_manager.unsubscribe_all()
+219        await self.ws_client().provider.disconnect()
 
@@ -1470,12 +1465,12 @@

-
226    def get_account_address(self) -> ChecksumAddress:
-227        """
-228        Get the address associated with the private key that this client
-229        was created with
-230        """
-231        return self.account.address
+            
221    def get_account_address(self) -> ChecksumAddress:
+222        """
+223        Get the address associated with the private key that this client
+224        was created with
+225        """
+226        return self.account.address
 
@@ -1496,13 +1491,13 @@

-
233    async def get_storage_value(self, entity_key: EntityKey) -> bytes:
-234        """
-235        Get the storage value stored in the given entity
-236        """
-237        return base64.b64decode(
-238            await self.http_client().eth.get_storage_value(entity_key.as_hex_string())
-239        )
+            
228    async def get_storage_value(self, entity_key: EntityKey) -> bytes:
+229        """
+230        Get the storage value stored in the given entity
+231        """
+232        return base64.b64decode(
+233            await self.http_client().eth.get_storage_value(entity_key.as_hex_string())
+234        )
 
@@ -1522,31 +1517,31 @@

-
241    async def get_entity_metadata(self, entity_key: EntityKey) -> EntityMetadata:
-242        """
-243        Get the metadata of the given entity
-244        """
-245        metadata = await self.http_client().eth.get_entity_metadata(
-246            entity_key.as_hex_string()
-247        )
-248
-249        return EntityMetadata(
-250            entity_key=entity_key,
-251            owner=Address(GenericBytes.from_hex_string(metadata.owner)),
-252            expires_at_block=metadata.expiresAtBlock,
-253            string_annotations=list(
-254                map(
-255                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
-256                    metadata.stringAnnotations,
-257                )
-258            ),
-259            numeric_annotations=list(
-260                map(
-261                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
-262                    metadata.numericAnnotations,
-263                )
-264            ),
-265        )
+            
236    async def get_entity_metadata(self, entity_key: EntityKey) -> EntityMetadata:
+237        """
+238        Get the metadata of the given entity
+239        """
+240        metadata = await self.http_client().eth.get_entity_metadata(
+241            entity_key.as_hex_string()
+242        )
+243
+244        return EntityMetadata(
+245            entity_key=entity_key,
+246            owner=Address(GenericBytes.from_hex_string(metadata.owner)),
+247            expires_at_block=metadata.expiresAtBlock,
+248            string_annotations=list(
+249                map(
+250                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
+251                    metadata.stringAnnotations,
+252                )
+253            ),
+254            numeric_annotations=list(
+255                map(
+256                    lambda ann: Annotation(key=ann["key"], value=ann["value"]),
+257                    metadata.numericAnnotations,
+258                )
+259            ),
+260        )
 
@@ -1560,26 +1555,26 @@

async def - get_entities_to_expire_at_block(self, block_number: int) -> Iterable[golem_base_sdk.types.EntityKey]: + get_entities_to_expire_at_block(self, block_number: int) -> Sequence[golem_base_sdk.types.EntityKey]:
-
267    async def get_entities_to_expire_at_block(
-268        self, block_number: int
-269    ) -> Iterable[EntityKey]:
-270        """
-271        Get all entities that will expire at the given block
-272        """
-273        return list(
-274            map(
-275                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
-276                await self.http_client().eth.get_entities_to_expire_at_block(
-277                    block_number
-278                ),
-279            )
-280        )
+            
262    async def get_entities_to_expire_at_block(
+263        self, block_number: int
+264    ) -> Sequence[EntityKey]:
+265        """
+266        Get all entities that will expire at the given block
+267        """
+268        return list(
+269            map(
+270                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
+271                await self.http_client().eth.get_entities_to_expire_at_block(
+272                    block_number
+273                ),
+274            )
+275        )
 
@@ -1599,11 +1594,11 @@

-
282    async def get_entity_count(self) -> int:
-283        """
-284        Get the total entity count in Golem Base
-285        """
-286        return await self.http_client().eth.get_entity_count()
+            
277    async def get_entity_count(self) -> int:
+278        """
+279        Get the total entity count in Golem Base
+280        """
+281        return await self.http_client().eth.get_entity_count()
 
@@ -1623,16 +1618,16 @@

-
288    async def get_all_entity_keys(self) -> Sequence[EntityKey]:
-289        """
-290        Get all entity keys in Golem Base
-291        """
-292        return list(
-293            map(
-294                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
-295                await self.http_client().eth.get_all_entity_keys(),
-296            )
-297        )
+            
283    async def get_all_entity_keys(self) -> Sequence[EntityKey]:
+284        """
+285        Get all entity keys in Golem Base
+286        """
+287        return list(
+288            map(
+289                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
+290                await self.http_client().eth.get_all_entity_keys(),
+291            )
+292        )
 
@@ -1646,24 +1641,26 @@

async def - get_entities_of_owner( self, owner: golem_base_sdk.types.Address) -> Sequence[golem_base_sdk.types.EntityKey]: + get_entities_of_owner( self, owner: eth_typing.evm.ChecksumAddress) -> Sequence[golem_base_sdk.types.EntityKey]:
-
299    async def get_entities_of_owner(self, owner: Address) -> Sequence[EntityKey]:
-300        """
-301        Get all the entities owned by the given address
-302        """
-303        return list(
-304            map(
-305                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
-306                # https://github.com/pylint-dev/pylint/issues/3162
-307                # pylint: disable=no-member
-308                await self.http_client().eth.get_entities_of_owner(owner),
-309            )
-310        )
+            
294    async def get_entities_of_owner(
+295        self, owner: ChecksumAddress
+296    ) -> Sequence[EntityKey]:
+297        """
+298        Get all the entities owned by the given address
+299        """
+300        return list(
+301            map(
+302                lambda e: EntityKey(GenericBytes.from_hex_string(e)),
+303                # https://github.com/pylint-dev/pylint/issues/3162
+304                # pylint: disable=no-member
+305                await self.http_client().eth.get_entities_of_owner(owner),
+306            )
+307        )
 
@@ -1683,18 +1680,18 @@

-
312    async def query_entities(self, query: str) -> Sequence[QueryEntitiesResult]:
-313        """
-314        Get all entities that satisfy the given Golem Base query
-315        """
-316        return list(
-317            map(
-318                lambda result: QueryEntitiesResult(
-319                    entity_key=result.key, storage_value=base64.b64decode(result.value)
-320                ),
-321                await self.http_client().eth.query_entities(query),
-322            )
-323        )
+            
309    async def query_entities(self, query: str) -> Sequence[QueryEntitiesResult]:
+310        """
+311        Get all entities that satisfy the given Golem Base query
+312        """
+313        return list(
+314            map(
+315                lambda result: QueryEntitiesResult(
+316                    entity_key=result.key, storage_value=base64.b64decode(result.value)
+317                ),
+318                await self.http_client().eth.query_entities(query),
+319            )
+320        )
 
@@ -1708,20 +1705,20 @@

async def - create_entities( self, creates: Sequence[golem_base_sdk.types.GolemBaseCreate]) -> Iterable[golem_base_sdk.types.CreateEntityReturnType]: + create_entities( self, creates: Sequence[golem_base_sdk.types.GolemBaseCreate]) -> Sequence[golem_base_sdk.types.CreateEntityReturnType]:
-
325    async def create_entities(
-326        self,
-327        creates: Sequence[GolemBaseCreate],
-328    ) -> Iterable[CreateEntityReturnType]:
-329        """
-330        Create entities in Golem Base
-331        """
-332        return (await self.send_transaction(creates=creates)).creates
+            
322    async def create_entities(
+323        self,
+324        creates: Sequence[GolemBaseCreate],
+325    ) -> Sequence[CreateEntityReturnType]:
+326        """
+327        Create entities in Golem Base
+328        """
+329        return (await self.send_transaction(creates=creates)).creates
 
@@ -1735,20 +1732,20 @@

async def - update_entities( self, updates: Sequence[golem_base_sdk.types.GolemBaseUpdate]) -> Iterable[golem_base_sdk.types.UpdateEntityReturnType]: + update_entities( self, updates: Sequence[golem_base_sdk.types.GolemBaseUpdate]) -> Sequence[golem_base_sdk.types.UpdateEntityReturnType]:
-
334    async def update_entities(
-335        self,
-336        updates: Sequence[GolemBaseUpdate],
-337    ) -> Iterable[UpdateEntityReturnType]:
-338        """
-339        Update entities in Golem Base
-340        """
-341        return (await self.send_transaction(updates=updates)).updates
+            
331    async def update_entities(
+332        self,
+333        updates: Sequence[GolemBaseUpdate],
+334    ) -> Sequence[UpdateEntityReturnType]:
+335        """
+336        Update entities in Golem Base
+337        """
+338        return (await self.send_transaction(updates=updates)).updates
 
@@ -1762,20 +1759,20 @@

async def - delete_entities( self, deletes: Sequence[golem_base_sdk.types.GolemBaseDelete]) -> Iterable[golem_base_sdk.types.EntityKey]: + delete_entities( self, deletes: Sequence[golem_base_sdk.types.GolemBaseDelete]) -> Sequence[golem_base_sdk.types.EntityKey]:
-
343    async def delete_entities(
-344        self,
-345        deletes: Sequence[GolemBaseDelete],
-346    ) -> Iterable[EntityKey]:
-347        """
-348        Delete entities from Golem Base
-349        """
-350        return (await self.send_transaction(deletes=deletes)).deletes
+            
340    async def delete_entities(
+341        self,
+342        deletes: Sequence[GolemBaseDelete],
+343    ) -> Sequence[EntityKey]:
+344        """
+345        Delete entities from Golem Base
+346        """
+347        return (await self.send_transaction(deletes=deletes)).deletes
 
@@ -1789,20 +1786,20 @@

async def - extend_entities( self, extensions: Sequence[golem_base_sdk.types.GolemBaseExtend]) -> Iterable[golem_base_sdk.types.ExtendEntityReturnType]: + extend_entities( self, extensions: Sequence[golem_base_sdk.types.GolemBaseExtend]) -> Sequence[golem_base_sdk.types.ExtendEntityReturnType]:
-
352    async def extend_entities(
-353        self,
-354        extensions: Sequence[GolemBaseExtend],
-355    ) -> Iterable[ExtendEntityReturnType]:
-356        """
-357        Extend the BTL of entities in Golem Base
-358        """
-359        return (await self.send_transaction(extensions=extensions)).extensions
+            
349    async def extend_entities(
+350        self,
+351        extensions: Sequence[GolemBaseExtend],
+352    ) -> Sequence[ExtendEntityReturnType]:
+353        """
+354        Extend the BTL of entities in Golem Base
+355        """
+356        return (await self.send_transaction(extensions=extensions)).extensions
 
@@ -1822,25 +1819,25 @@

-
361    async def send_transaction(
-362        self,
-363        creates: Optional[Sequence[GolemBaseCreate]] = None,
-364        updates: Optional[Sequence[GolemBaseUpdate]] = None,
-365        deletes: Optional[Sequence[GolemBaseDelete]] = None,
-366        extensions: Optional[Sequence[GolemBaseExtend]] = None,
-367    ) -> GolemBaseTransactionReceipt:
-368        """
-369        Send a generic transaction to Golem Base.
-370        This transaction can contain multiple create, update, delete and
-371        extend operations
-372        """
-373        tx = GolemBaseTransaction(
-374            creates,
-375            updates,
-376            deletes,
-377            extensions,
-378        )
-379        return await self._send_gb_transaction(tx)
+            
358    async def send_transaction(
+359        self,
+360        creates: Optional[Sequence[GolemBaseCreate]] = None,
+361        updates: Optional[Sequence[GolemBaseUpdate]] = None,
+362        deletes: Optional[Sequence[GolemBaseDelete]] = None,
+363        extensions: Optional[Sequence[GolemBaseExtend]] = None,
+364    ) -> GolemBaseTransactionReceipt:
+365        """
+366        Send a generic transaction to Golem Base.
+367        This transaction can contain multiple create, update, delete and
+368        extend operations
+369        """
+370        tx = GolemBaseTransaction(
+371            creates,
+372            updates,
+373            deletes,
+374            extensions,
+375        )
+376        return await self._send_gb_transaction(tx)
 
@@ -1862,69 +1859,69 @@

-
507    async def watch_logs(
-508        self,
-509        create_callback: Callable[[CreateEntityReturnType], None],
-510        update_callback: Callable[[UpdateEntityReturnType], None],
-511        delete_callback: Callable[[EntityKey], None],
-512        extend_callback: Callable[[ExtendEntityReturnType], None],
-513    ) -> None:
-514        """
-515        Subscribe to events on Golem Base.
-516        You can pass in four different callbacks, and the right one will
-517        be invoked for every create, update, delete, and extend operation.
-518        """
-519
-520        async def log_handler(
-521            handler_context: LogsSubscriptionContext,
-522        ) -> None:
-523            # We only use this handler for log receipts
-524            # TypeDicts cannot be checked at runtime
-525            log_receipt = typing.cast(LogReceipt, handler_context.result)
-526            logger.debug("New log: %s", log_receipt)
-527            res = await self._process_golem_base_log_receipt(log_receipt)
-528
-529            for create in res.creates:
-530                create_callback(create)
-531            for update in res.updates:
-532                update_callback(update)
-533            for key in res.deletes:
-534                delete_callback(key)
-535            for extension in res.extensions:
-536                extend_callback(extension)
-537
-538        def create_subscription(topic: HexStr) -> LogsSubscription:
-539            return LogsSubscription(
-540                label=f"Golem Base subscription to topic {topic}",
-541                address=self.golem_base_contract.address,
-542                topics=[topic],
-543                handler=log_handler,
-544                # optional `handler_context` args to help parse a response
-545                handler_context={},
-546            )
-547
-548        async def handle_subscriptions() -> None:
-549            await self._ws_client.subscription_manager.subscribe(
-550                list(
-551                    map(
-552                        lambda event: create_subscription(event.topic),
-553                        self.golem_base_contract.all_events(),
-554                    )
-555                ),
-556            )
-557            # handle subscriptions via configured handlers:
-558            await self.ws_client().subscription_manager.handle_subscriptions()
-559
-560        # Create a long running task to handle subscriptions that we can run on
-561        # the asyncio event loop
-562        task = asyncio.create_task(handle_subscriptions())
-563        self._background_tasks.add(task)
-564
-565        def task_done(task: asyncio.Task) -> None:
-566            logger.info("Subscription background task done, removing...")
-567            self._background_tasks.discard(task)
-568
-569        task.add_done_callback(task_done)
+            
504    async def watch_logs(
+505        self,
+506        create_callback: Callable[[CreateEntityReturnType], None],
+507        update_callback: Callable[[UpdateEntityReturnType], None],
+508        delete_callback: Callable[[EntityKey], None],
+509        extend_callback: Callable[[ExtendEntityReturnType], None],
+510    ) -> None:
+511        """
+512        Subscribe to events on Golem Base.
+513        You can pass in four different callbacks, and the right one will
+514        be invoked for every create, update, delete, and extend operation.
+515        """
+516
+517        async def log_handler(
+518            handler_context: LogsSubscriptionContext,
+519        ) -> None:
+520            # We only use this handler for log receipts
+521            # TypeDicts cannot be checked at runtime
+522            log_receipt = typing.cast(LogReceipt, handler_context.result)
+523            logger.debug("New log: %s", log_receipt)
+524            res = await self._process_golem_base_log_receipt(log_receipt)
+525
+526            for create in res.creates:
+527                create_callback(create)
+528            for update in res.updates:
+529                update_callback(update)
+530            for key in res.deletes:
+531                delete_callback(key)
+532            for extension in res.extensions:
+533                extend_callback(extension)
+534
+535        def create_subscription(topic: HexStr) -> LogsSubscription:
+536            return LogsSubscription(
+537                label=f"Golem Base subscription to topic {topic}",
+538                address=self.golem_base_contract.address,
+539                topics=[topic],
+540                handler=log_handler,
+541                # optional `handler_context` args to help parse a response
+542                handler_context={},
+543            )
+544
+545        async def handle_subscriptions() -> None:
+546            await self._ws_client.subscription_manager.subscribe(
+547                list(
+548                    map(
+549                        lambda event: create_subscription(event.topic),
+550                        self.golem_base_contract.all_events(),
+551                    )
+552                ),
+553            )
+554            # handle subscriptions via configured handlers:
+555            await self.ws_client().subscription_manager.handle_subscriptions()
+556
+557        # Create a long running task to handle subscriptions that we can run on
+558        # the asyncio event loop
+559        task = asyncio.create_task(handle_subscriptions())
+560        self._background_tasks.add(task)
+561
+562        def task_done(task: asyncio.Task) -> None:
+563            logger.info("Subscription background task done, removing...")
+564            self._background_tasks.discard(task)
+565
+566        task.add_done_callback(task_done)
 
diff --git a/docs/golem_base_sdk/constants.html b/docs/golem_base_sdk/constants.html index f163f0a..106e031 100644 --- a/docs/golem_base_sdk/constants.html +++ b/docs/golem_base_sdk/constants.html @@ -63,79 +63,81 @@

2Constants used in the Golem Base SDK 3""" 4 - 5from .types import ( - 6 Address, - 7 GenericBytes, - 8) - 9 -10STORAGE_ADDRESS: Address = Address( -11 GenericBytes.from_hex_string("0x0000000000000000000000000000000060138453") -12) -13 -14GOLEM_BASE_ABI = [ -15 { -16 "anonymous": False, -17 "inputs": [ -18 {"indexed": True, "name": "entityKey", "type": "uint256"}, -19 {"indexed": False, "name": "expirationBlock", "type": "uint256"}, -20 ], -21 "name": "GolemBaseStorageEntityCreated", -22 "type": "event", -23 }, -24 { -25 "anonymous": False, -26 "inputs": [ -27 {"indexed": True, "name": "entityKey", "type": "uint256"}, -28 {"indexed": False, "name": "expirationBlock", "type": "uint256"}, -29 ], -30 "name": "GolemBaseStorageEntityUpdated", -31 "type": "event", -32 }, -33 { -34 "anonymous": False, -35 "inputs": [{"indexed": True, "name": "entityKey", "type": "uint256"}], -36 "name": "GolemBaseStorageEntityDeleted", -37 "type": "event", -38 }, -39 { -40 "anonymous": False, -41 "inputs": [ -42 {"indexed": True, "name": "entityKey", "type": "uint256"}, -43 {"indexed": False, "name": "oldExpirationBlock", "type": "uint256"}, -44 {"indexed": False, "name": "newExpirationBlock", "type": "uint256"}, -45 ], -46 "name": "GolemBaseStorageEntityBTLExtended", -47 "type": "event", -48 }, -49 # Old ABI event that has a typo in the name and a missing non-indexed argument. -50 # This can be removed once we retire the kaolin network (the only one using this event hash). -51 { -52 "anonymous": False, -53 "inputs": [ -54 {"indexed": True, "name": "entityKey", "type": "uint256"}, -55 {"indexed": False, "name": "expirationBlock", "type": "uint256"}, -56 ], -57 "name": "GolemBaseStorageEntityBTLExptended", -58 "type": "event", -59 }, -60 # Old ABI before rename of TTL -> BTL -61 { -62 "anonymous": False, -63 "inputs": [ -64 {"indexed": True, "name": "entityKey", "type": "uint256"}, -65 {"indexed": False, "name": "expirationBlock", "type": "uint256"}, -66 ], -67 "name": "GolemBaseStorageEntityTTLExptended", -68 "type": "event", -69 }, -70] + 5from typing import Any, Final, Sequence + 6 + 7from .types import ( + 8 Address, + 9 GenericBytes, +10) +11 +12STORAGE_ADDRESS: Final[Address] = Address( +13 GenericBytes.from_hex_string("0x0000000000000000000000000000000060138453") +14) +15 +16GOLEM_BASE_ABI: Final[Sequence[dict[str, Any]]] = [ +17 { +18 "anonymous": False, +19 "inputs": [ +20 {"indexed": True, "name": "entityKey", "type": "uint256"}, +21 {"indexed": False, "name": "expirationBlock", "type": "uint256"}, +22 ], +23 "name": "GolemBaseStorageEntityCreated", +24 "type": "event", +25 }, +26 { +27 "anonymous": False, +28 "inputs": [ +29 {"indexed": True, "name": "entityKey", "type": "uint256"}, +30 {"indexed": False, "name": "expirationBlock", "type": "uint256"}, +31 ], +32 "name": "GolemBaseStorageEntityUpdated", +33 "type": "event", +34 }, +35 { +36 "anonymous": False, +37 "inputs": [{"indexed": True, "name": "entityKey", "type": "uint256"}], +38 "name": "GolemBaseStorageEntityDeleted", +39 "type": "event", +40 }, +41 { +42 "anonymous": False, +43 "inputs": [ +44 {"indexed": True, "name": "entityKey", "type": "uint256"}, +45 {"indexed": False, "name": "oldExpirationBlock", "type": "uint256"}, +46 {"indexed": False, "name": "newExpirationBlock", "type": "uint256"}, +47 ], +48 "name": "GolemBaseStorageEntityBTLExtended", +49 "type": "event", +50 }, +51 # Old ABI event that has a typo in the name and a missing non-indexed argument. +52 # This can be removed once we retire the kaolin network (the only one using this event hash). +53 { +54 "anonymous": False, +55 "inputs": [ +56 {"indexed": True, "name": "entityKey", "type": "uint256"}, +57 {"indexed": False, "name": "expirationBlock", "type": "uint256"}, +58 ], +59 "name": "GolemBaseStorageEntityBTLExptended", +60 "type": "event", +61 }, +62 # Old ABI before rename of TTL -> BTL +63 { +64 "anonymous": False, +65 "inputs": [ +66 {"indexed": True, "name": "entityKey", "type": "uint256"}, +67 {"indexed": False, "name": "expirationBlock", "type": "uint256"}, +68 ], +69 "name": "GolemBaseStorageEntityTTLExptended", +70 "type": "event", +71 }, +72]

- STORAGE_ADDRESS: golem_base_sdk.types.Address = + STORAGE_ADDRESS: Final[golem_base_sdk.types.Address] = GenericBytes(0x0000000000000000000000000000000060138453) @@ -147,7 +149,7 @@

- GOLEM_BASE_ABI = + GOLEM_BASE_ABI: Final[Sequence[dict[str, Any]]] = [{'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityCreated', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityUpdated', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityDeleted', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'oldExpirationBlock', 'type': 'uint256'}, {'indexed': False, 'name': 'newExpirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityBTLExtended', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityBTLExptended', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityTTLExptended', 'type': 'event'}] diff --git a/docs/golem_base_sdk/utils.html b/docs/golem_base_sdk/utils.html index b7a50f7..63c0443 100644 --- a/docs/golem_base_sdk/utils.html +++ b/docs/golem_base_sdk/utils.html @@ -121,56 +121,54 @@

60 # Delete 61 list( 62 map( - 63 lambda el: [ - 64 el.entity_key.generic_bytes, - 65 ], - 66 tx.deletes, - 67 ) - 68 ), - 69 # Extend - 70 list( - 71 map( - 72 lambda el: [ - 73 el.entity_key.generic_bytes, - 74 el.number_of_blocks, - 75 ], - 76 tx.extensions, - 77 ) - 78 ), - 79 ] - 80 logger.debug("Payload before RLP encoding: %s", payload) - 81 encoded = rlp.encode(payload) - 82 logger.debug(encoded) - 83 return encoded - 84 - 85 - 86def parse_legacy_btl_extended_log(log_receipt: LogReceipt) -> ExtendEntityReturnType: - 87 """ - 88 For legacy extend ABI types, the type signature in the ABI does - 89 not correspond to the actual data returned, so we need - 90 to parse the data ourselves. - 91 """ - 92 # pylint: disable=line-too-long - 93 # Take the first 64 bytes by masking the rest - 94 # (shift 1 to the left 256 positions, then negate the number) - 95 # Example: - 96 # 0x 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 012f - 97 # 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0143 - 98 # mask this with: - 99 # 0x 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -100 # 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 -101 # to obtain 0x143 -102 # and then shift the original number to the right -103 # by 256 to obtain 0x12f -104 data_parsed = int.from_bytes(log_receipt["data"], byteorder="big", signed=False) -105 new_expiration_block = data_parsed & ((1 << 256) - 1) -106 old_expiration_block = data_parsed >> 256 -107 -108 return ExtendEntityReturnType( -109 old_expiration_block=old_expiration_block, -110 new_expiration_block=new_expiration_block, -111 entity_key=EntityKey(GenericBytes(log_receipt["topics"][1])), -112 ) + 63 lambda el: el.entity_key.generic_bytes, + 64 tx.deletes, + 65 ) + 66 ), + 67 # Extend + 68 list( + 69 map( + 70 lambda el: [ + 71 el.entity_key.generic_bytes, + 72 el.number_of_blocks, + 73 ], + 74 tx.extensions, + 75 ) + 76 ), + 77 ] + 78 logger.debug("Payload before RLP encoding: %s", payload) + 79 encoded = rlp.encode(payload) + 80 logger.debug("Encoded payload: %s", encoded) + 81 return encoded + 82 + 83 + 84def parse_legacy_btl_extended_log(log_receipt: LogReceipt) -> ExtendEntityReturnType: + 85 """ + 86 For legacy extend ABI types, the type signature in the ABI does + 87 not correspond to the actual data returned, so we need + 88 to parse the data ourselves. + 89 """ + 90 # pylint: disable=line-too-long + 91 # Take the first 64 bytes by masking the rest + 92 # (shift 1 to the left 256 positions, then negate the number) + 93 # Example: + 94 # 0x 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 012f + 95 # 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0143 + 96 # mask this with: + 97 # 0x 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 + 98 # 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 + 99 # to obtain 0x143 +100 # and then shift the original number to the right +101 # by 256 to obtain 0x12f +102 data_parsed = int.from_bytes(log_receipt["data"], byteorder="big", signed=False) +103 new_expiration_block = data_parsed & ((1 << 256) - 1) +104 old_expiration_block = data_parsed >> 256 +105 +106 return ExtendEntityReturnType( +107 old_expiration_block=old_expiration_block, +108 new_expiration_block=new_expiration_block, +109 entity_key=EntityKey(GenericBytes(log_receipt["topics"][1])), +110 )

@@ -225,27 +223,25 @@

61 # Delete 62 list( 63 map( -64 lambda el: [ -65 el.entity_key.generic_bytes, -66 ], -67 tx.deletes, -68 ) -69 ), -70 # Extend -71 list( -72 map( -73 lambda el: [ -74 el.entity_key.generic_bytes, -75 el.number_of_blocks, -76 ], -77 tx.extensions, -78 ) -79 ), -80 ] -81 logger.debug("Payload before RLP encoding: %s", payload) -82 encoded = rlp.encode(payload) -83 logger.debug(encoded) -84 return encoded +64 lambda el: el.entity_key.generic_bytes, +65 tx.deletes, +66 ) +67 ), +68 # Extend +69 list( +70 map( +71 lambda el: [ +72 el.entity_key.generic_bytes, +73 el.number_of_blocks, +74 ], +75 tx.extensions, +76 ) +77 ), +78 ] +79 logger.debug("Payload before RLP encoding: %s", payload) +80 encoded = rlp.encode(payload) +81 logger.debug("Encoded payload: %s", encoded) +82 return encoded

@@ -265,33 +261,33 @@

-
 87def parse_legacy_btl_extended_log(log_receipt: LogReceipt) -> ExtendEntityReturnType:
- 88    """
- 89    For legacy extend ABI types, the type signature in the ABI does
- 90    not correspond to the actual data returned, so we need
- 91    to parse the data ourselves.
- 92    """
- 93    # pylint: disable=line-too-long
- 94    # Take the first 64 bytes by masking the rest
- 95    # (shift 1 to the left 256 positions, then negate the number)
- 96    # Example:
- 97    # 0x 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 012f
- 98    #    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0143
- 99    # mask this with:
-100    # 0x 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
-101    #    1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
-102    # to obtain 0x143
-103    # and then shift the original number to the right
-104    # by 256 to obtain 0x12f
-105    data_parsed = int.from_bytes(log_receipt["data"], byteorder="big", signed=False)
-106    new_expiration_block = data_parsed & ((1 << 256) - 1)
-107    old_expiration_block = data_parsed >> 256
-108
-109    return ExtendEntityReturnType(
-110        old_expiration_block=old_expiration_block,
-111        new_expiration_block=new_expiration_block,
-112        entity_key=EntityKey(GenericBytes(log_receipt["topics"][1])),
-113    )
+            
 85def parse_legacy_btl_extended_log(log_receipt: LogReceipt) -> ExtendEntityReturnType:
+ 86    """
+ 87    For legacy extend ABI types, the type signature in the ABI does
+ 88    not correspond to the actual data returned, so we need
+ 89    to parse the data ourselves.
+ 90    """
+ 91    # pylint: disable=line-too-long
+ 92    # Take the first 64 bytes by masking the rest
+ 93    # (shift 1 to the left 256 positions, then negate the number)
+ 94    # Example:
+ 95    # 0x 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 012f
+ 96    #    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0143
+ 97    # mask this with:
+ 98    # 0x 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+ 99    #    1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
+100    # to obtain 0x143
+101    # and then shift the original number to the right
+102    # by 256 to obtain 0x12f
+103    data_parsed = int.from_bytes(log_receipt["data"], byteorder="big", signed=False)
+104    new_expiration_block = data_parsed & ((1 << 256) - 1)
+105    old_expiration_block = data_parsed >> 256
+106
+107    return ExtendEntityReturnType(
+108        old_expiration_block=old_expiration_block,
+109        new_expiration_block=new_expiration_block,
+110        entity_key=EntityKey(GenericBytes(log_receipt["topics"][1])),
+111    )
 
diff --git a/docs/search.js b/docs/search.js index a2c4cce..11413af 100644 --- a/docs/search.js +++ b/docs/search.js @@ -1,6 +1,6 @@ window.pdocSearch = (function(){ /** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();oGolemBase Python SDK

\n"}, {"fullname": "golem_base_sdk.GolemBaseClient", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient", "kind": "class", "doc": "

The Golem Base client used to interact with Golem Base.\nMany useful methods are implemented directly on this type, while more\ngeneric ethereum methods can be accessed through the underlying\nweb3 client that you can access with the\nGolemBaseClient.http_client()\nmethod.

\n"}, {"fullname": "golem_base_sdk.GolemBaseClient.__init__", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.__init__", "kind": "function", "doc": "

\n", "signature": "(\trpc_url: str,\tws_client: web3.main.AsyncWeb3,\tprivate_key: Sequence[bytes])"}, {"fullname": "golem_base_sdk.GolemBaseClient.create", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.create", "kind": "function", "doc": "

Static method to create a GolemBaseClient instance,\nthis is the preferred method to create an instance.

\n", "signature": "(\trpc_url: str,\tws_url: str,\tprivate_key: Sequence[bytes]) -> golem_base_sdk.GolemBaseClient:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.account", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.account", "kind": "variable", "doc": "

\n"}, {"fullname": "golem_base_sdk.GolemBaseClient.golem_base_contract", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.golem_base_contract", "kind": "variable", "doc": "

\n"}, {"fullname": "golem_base_sdk.GolemBaseClient.http_client", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.http_client", "kind": "function", "doc": "

Get the underlying web3 http client

\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "golem_base_sdk.GolemBaseClient.ws_client", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.ws_client", "kind": "function", "doc": "

Get the underlying web3 websocket client

\n", "signature": "(self) -> web3.main.AsyncWeb3:", "funcdef": "def"}, {"fullname": "golem_base_sdk.GolemBaseClient.is_connected", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.is_connected", "kind": "function", "doc": "

Check whether the client's underlying http client is connected

\n", "signature": "(self) -> bool:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.disconnect", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.disconnect", "kind": "function", "doc": "

Disconnect both the underlying http and ws clients and\nunsubscribe from all subscriptions

\n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_account_address", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_account_address", "kind": "function", "doc": "

Get the address associated with the private key that this client\nwas created with

\n", "signature": "(self) -> eth_typing.evm.ChecksumAddress:", "funcdef": "def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_storage_value", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_storage_value", "kind": "function", "doc": "

Get the storage value stored in the given entity

\n", "signature": "(self, entity_key: golem_base_sdk.types.EntityKey) -> bytes:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_entity_metadata", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_entity_metadata", "kind": "function", "doc": "

Get the metadata of the given entity

\n", "signature": "(\tself,\tentity_key: golem_base_sdk.types.EntityKey) -> golem_base_sdk.types.EntityMetadata:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_entities_to_expire_at_block", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_entities_to_expire_at_block", "kind": "function", "doc": "

Get all entities that will expire at the given block

\n", "signature": "(self, block_number: int) -> Iterable[golem_base_sdk.types.EntityKey]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_entity_count", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_entity_count", "kind": "function", "doc": "

Get the total entity count in Golem Base

\n", "signature": "(self) -> int:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_all_entity_keys", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_all_entity_keys", "kind": "function", "doc": "

Get all entity keys in Golem Base

\n", "signature": "(self) -> Sequence[golem_base_sdk.types.EntityKey]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_entities_of_owner", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_entities_of_owner", "kind": "function", "doc": "

Get all the entities owned by the given address

\n", "signature": "(\tself,\towner: golem_base_sdk.types.Address) -> Sequence[golem_base_sdk.types.EntityKey]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.query_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.query_entities", "kind": "function", "doc": "

Get all entities that satisfy the given Golem Base query

\n", "signature": "(self, query: str) -> Sequence[golem_base_sdk.types.QueryEntitiesResult]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.create_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.create_entities", "kind": "function", "doc": "

Create entities in Golem Base

\n", "signature": "(\tself,\tcreates: Sequence[golem_base_sdk.types.GolemBaseCreate]) -> Iterable[golem_base_sdk.types.CreateEntityReturnType]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.update_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.update_entities", "kind": "function", "doc": "

Update entities in Golem Base

\n", "signature": "(\tself,\tupdates: Sequence[golem_base_sdk.types.GolemBaseUpdate]) -> Iterable[golem_base_sdk.types.UpdateEntityReturnType]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.delete_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.delete_entities", "kind": "function", "doc": "

Delete entities from Golem Base

\n", "signature": "(\tself,\tdeletes: Sequence[golem_base_sdk.types.GolemBaseDelete]) -> Iterable[golem_base_sdk.types.EntityKey]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.extend_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.extend_entities", "kind": "function", "doc": "

Extend the BTL of entities in Golem Base

\n", "signature": "(\tself,\textensions: Sequence[golem_base_sdk.types.GolemBaseExtend]) -> Iterable[golem_base_sdk.types.ExtendEntityReturnType]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.send_transaction", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.send_transaction", "kind": "function", "doc": "

Send a generic transaction to Golem Base.\nThis transaction can contain multiple create, update, delete and\nextend operations

\n", "signature": "(\tself,\tcreates: Optional[Sequence[golem_base_sdk.types.GolemBaseCreate]] = None,\tupdates: Optional[Sequence[golem_base_sdk.types.GolemBaseUpdate]] = None,\tdeletes: Optional[Sequence[golem_base_sdk.types.GolemBaseDelete]] = None,\textensions: Optional[Sequence[golem_base_sdk.types.GolemBaseExtend]] = None) -> golem_base_sdk.types.GolemBaseTransactionReceipt:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.watch_logs", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.watch_logs", "kind": "function", "doc": "

Subscribe to events on Golem Base.\nYou can pass in four different callbacks, and the right one will\nbe invoked for every create, update, delete, and extend operation.

\n", "signature": "(\tself,\tcreate_callback: Callable[[golem_base_sdk.types.CreateEntityReturnType], NoneType],\tupdate_callback: Callable[[golem_base_sdk.types.UpdateEntityReturnType], NoneType],\tdelete_callback: Callable[[golem_base_sdk.types.EntityKey], NoneType],\textend_callback: Callable[[golem_base_sdk.types.ExtendEntityReturnType], NoneType]) -> None:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.constants", "modulename": "golem_base_sdk.constants", "kind": "module", "doc": "

Constants used in the Golem Base SDK

\n"}, {"fullname": "golem_base_sdk.constants.STORAGE_ADDRESS", "modulename": "golem_base_sdk.constants", "qualname": "STORAGE_ADDRESS", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.Address", "default_value": "GenericBytes(0x0000000000000000000000000000000060138453)"}, {"fullname": "golem_base_sdk.constants.GOLEM_BASE_ABI", "modulename": "golem_base_sdk.constants", "qualname": "GOLEM_BASE_ABI", "kind": "variable", "doc": "

\n", "default_value": "[{'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityCreated', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityUpdated', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityDeleted', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'oldExpirationBlock', 'type': 'uint256'}, {'indexed': False, 'name': 'newExpirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityBTLExtended', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityBTLExptended', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityTTLExptended', 'type': 'event'}]"}, {"fullname": "golem_base_sdk.types", "modulename": "golem_base_sdk.types", "kind": "module", "doc": "

Golem Base SDK Types

\n"}, {"fullname": "golem_base_sdk.types.GenericBytes", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes", "kind": "class", "doc": "

Class to represent bytes that can be converted to more meaningful types

\n"}, {"fullname": "golem_base_sdk.types.GenericBytes.__init__", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.__init__", "kind": "function", "doc": "

\n", "signature": "(generic_bytes: bytes)"}, {"fullname": "golem_base_sdk.types.GenericBytes.generic_bytes", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.generic_bytes", "kind": "variable", "doc": "

\n", "annotation": ": bytes"}, {"fullname": "golem_base_sdk.types.GenericBytes.as_hex_string", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.as_hex_string", "kind": "function", "doc": "

Convert this instance to a hexadecimal string

\n", "signature": "(self) -> eth_typing.encoding.HexStr:", "funcdef": "def"}, {"fullname": "golem_base_sdk.types.GenericBytes.as_address", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.as_address", "kind": "function", "doc": "

Convert this instance to a eth_typing.ChecksumAddress

\n", "signature": "(self) -> eth_typing.evm.ChecksumAddress:", "funcdef": "def"}, {"fullname": "golem_base_sdk.types.GenericBytes.from_hex_string", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.from_hex_string", "kind": "function", "doc": "

Create a GenericBytes instance from a hexadecimal string

\n", "signature": "(hexstr: str) -> golem_base_sdk.types.GenericBytes:", "funcdef": "def"}, {"fullname": "golem_base_sdk.types.EntityKey", "modulename": "golem_base_sdk.types", "qualname": "EntityKey", "kind": "variable", "doc": "

\n", "default_value": "golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.Address", "modulename": "golem_base_sdk.types", "qualname": "Address", "kind": "variable", "doc": "

\n", "default_value": "golem_base_sdk.types.Address"}, {"fullname": "golem_base_sdk.types.Annotation", "modulename": "golem_base_sdk.types", "qualname": "Annotation", "kind": "class", "doc": "

Class to represent generic annotations

\n", "bases": "typing.Generic[V]"}, {"fullname": "golem_base_sdk.types.Annotation.__init__", "modulename": "golem_base_sdk.types", "qualname": "Annotation.__init__", "kind": "function", "doc": "

\n", "signature": "(key: str, value: V)"}, {"fullname": "golem_base_sdk.types.Annotation.key", "modulename": "golem_base_sdk.types", "qualname": "Annotation.key", "kind": "variable", "doc": "

\n", "annotation": ": str"}, {"fullname": "golem_base_sdk.types.Annotation.value", "modulename": "golem_base_sdk.types", "qualname": "Annotation.value", "kind": "variable", "doc": "

\n", "annotation": ": V"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate", "kind": "class", "doc": "

Class to represent a create operation in Golem Base

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.__init__", "kind": "function", "doc": "

\n", "signature": "(\tdata: bytes,\tttl: int,\tstring_annotations: Sequence[golem_base_sdk.types.Annotation[str]],\tnumeric_annotations: Sequence[golem_base_sdk.types.Annotation[int]])"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.data", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.data", "kind": "variable", "doc": "

\n", "annotation": ": bytes"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.ttl", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.ttl", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.string_annotations", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.string_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[str]]"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.numeric_annotations", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.numeric_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[int]]"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate", "kind": "class", "doc": "

Class to represent an update operation in Golem Base

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.__init__", "kind": "function", "doc": "

\n", "signature": "(\tentity_key: golem_base_sdk.types.EntityKey,\tdata: bytes,\tttl: int,\tstring_annotations: Sequence[golem_base_sdk.types.Annotation[str]],\tnumeric_annotations: Sequence[golem_base_sdk.types.Annotation[int]])"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.entity_key", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.data", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.data", "kind": "variable", "doc": "

\n", "annotation": ": bytes"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.ttl", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.ttl", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.string_annotations", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.string_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[str]]"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.numeric_annotations", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.numeric_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[int]]"}, {"fullname": "golem_base_sdk.types.GolemBaseDelete", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseDelete", "kind": "class", "doc": "

Class to represent a delete operation in Golem Base

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseDelete.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseDelete.__init__", "kind": "function", "doc": "

\n", "signature": "(entity_key: golem_base_sdk.types.EntityKey)"}, {"fullname": "golem_base_sdk.types.GolemBaseDelete.entity_key", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseDelete.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.GolemBaseExtend", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseExtend", "kind": "class", "doc": "

Class to represent a BTL extend operation in Golem Base

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseExtend.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseExtend.__init__", "kind": "function", "doc": "

\n", "signature": "(entity_key: golem_base_sdk.types.EntityKey, number_of_blocks: int)"}, {"fullname": "golem_base_sdk.types.GolemBaseExtend.entity_key", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseExtend.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.GolemBaseExtend.number_of_blocks", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseExtend.number_of_blocks", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction", "kind": "class", "doc": "

Class to represent a transaction in Golem Base.\nA transaction consist of one or more\nGolemBaseCreate,\nGolemBaseUpdate,\nGolemBaseDelete and\nGolemBaseExtend\noperations.

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.__init__", "kind": "function", "doc": "

\n", "signature": "(\tcreates: Optional[Sequence[golem_base_sdk.types.GolemBaseCreate]] = None,\tupdates: Optional[Sequence[golem_base_sdk.types.GolemBaseUpdate]] = None,\tdeletes: Optional[Sequence[golem_base_sdk.types.GolemBaseDelete]] = None,\textensions: Optional[Sequence[golem_base_sdk.types.GolemBaseExtend]] = None)"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.creates", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.creates", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.GolemBaseCreate]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.updates", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.updates", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.GolemBaseUpdate]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.deletes", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.deletes", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.GolemBaseDelete]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.extensions", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.extensions", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.GolemBaseExtend]"}, {"fullname": "golem_base_sdk.types.CreateEntityReturnType", "modulename": "golem_base_sdk.types", "qualname": "CreateEntityReturnType", "kind": "class", "doc": "

The return type of a Golem Base create operation

\n"}, {"fullname": "golem_base_sdk.types.CreateEntityReturnType.__init__", "modulename": "golem_base_sdk.types", "qualname": "CreateEntityReturnType.__init__", "kind": "function", "doc": "

\n", "signature": "(expiration_block: int, entity_key: golem_base_sdk.types.EntityKey)"}, {"fullname": "golem_base_sdk.types.CreateEntityReturnType.expiration_block", "modulename": "golem_base_sdk.types", "qualname": "CreateEntityReturnType.expiration_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.CreateEntityReturnType.entity_key", "modulename": "golem_base_sdk.types", "qualname": "CreateEntityReturnType.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.UpdateEntityReturnType", "modulename": "golem_base_sdk.types", "qualname": "UpdateEntityReturnType", "kind": "class", "doc": "

The return type of a Golem Base update operation

\n"}, {"fullname": "golem_base_sdk.types.UpdateEntityReturnType.__init__", "modulename": "golem_base_sdk.types", "qualname": "UpdateEntityReturnType.__init__", "kind": "function", "doc": "

\n", "signature": "(expiration_block: int, entity_key: golem_base_sdk.types.EntityKey)"}, {"fullname": "golem_base_sdk.types.UpdateEntityReturnType.expiration_block", "modulename": "golem_base_sdk.types", "qualname": "UpdateEntityReturnType.expiration_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.UpdateEntityReturnType.entity_key", "modulename": "golem_base_sdk.types", "qualname": "UpdateEntityReturnType.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType", "kind": "class", "doc": "

The return type of a Golem Base extend operation

\n"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType.__init__", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType.__init__", "kind": "function", "doc": "

\n", "signature": "(\told_expiration_block: int,\tnew_expiration_block: int,\tentity_key: golem_base_sdk.types.EntityKey)"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType.old_expiration_block", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType.old_expiration_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType.new_expiration_block", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType.new_expiration_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType.entity_key", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt", "kind": "class", "doc": "

The return type of a Golem Base transaction

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.__init__", "kind": "function", "doc": "

\n", "signature": "(\tcreates: Sequence[golem_base_sdk.types.CreateEntityReturnType],\tupdates: Sequence[golem_base_sdk.types.UpdateEntityReturnType],\textensions: Sequence[golem_base_sdk.types.ExtendEntityReturnType],\tdeletes: Sequence[golem_base_sdk.types.EntityKey])"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.creates", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.creates", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.CreateEntityReturnType]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.updates", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.updates", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.UpdateEntityReturnType]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.extensions", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.extensions", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.ExtendEntityReturnType]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.deletes", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.deletes", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.EntityKey]"}, {"fullname": "golem_base_sdk.types.EntityMetadata", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata", "kind": "class", "doc": "

A class representing entity metadata

\n"}, {"fullname": "golem_base_sdk.types.EntityMetadata.__init__", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.__init__", "kind": "function", "doc": "

\n", "signature": "(\tentity_key: golem_base_sdk.types.EntityKey,\towner: golem_base_sdk.types.Address,\texpires_at_block: int,\tstring_annotations: Sequence[golem_base_sdk.types.Annotation[str]],\tnumeric_annotations: Sequence[golem_base_sdk.types.Annotation[int]])"}, {"fullname": "golem_base_sdk.types.EntityMetadata.entity_key", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.EntityMetadata.owner", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.owner", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.Address"}, {"fullname": "golem_base_sdk.types.EntityMetadata.expires_at_block", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.expires_at_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.EntityMetadata.string_annotations", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.string_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[str]]"}, {"fullname": "golem_base_sdk.types.EntityMetadata.numeric_annotations", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.numeric_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[int]]"}, {"fullname": "golem_base_sdk.types.QueryEntitiesResult", "modulename": "golem_base_sdk.types", "qualname": "QueryEntitiesResult", "kind": "class", "doc": "

A class representing the return value of a Golem Base query

\n"}, {"fullname": "golem_base_sdk.types.QueryEntitiesResult.__init__", "modulename": "golem_base_sdk.types", "qualname": "QueryEntitiesResult.__init__", "kind": "function", "doc": "

\n", "signature": "(entity_key: golem_base_sdk.types.EntityKey, storage_value: bytes)"}, {"fullname": "golem_base_sdk.types.QueryEntitiesResult.entity_key", "modulename": "golem_base_sdk.types", "qualname": "QueryEntitiesResult.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.QueryEntitiesResult.storage_value", "modulename": "golem_base_sdk.types", "qualname": "QueryEntitiesResult.storage_value", "kind": "variable", "doc": "

\n", "annotation": ": bytes"}, {"fullname": "golem_base_sdk.utils", "modulename": "golem_base_sdk.utils", "kind": "module", "doc": "

Utility methods

\n"}, {"fullname": "golem_base_sdk.utils.rlp_encode_transaction", "modulename": "golem_base_sdk.utils", "qualname": "rlp_encode_transaction", "kind": "function", "doc": "

Encode a Golem Base transaction in RLP

\n", "signature": "(tx: golem_base_sdk.types.GolemBaseTransaction) -> bytes:", "funcdef": "def"}, {"fullname": "golem_base_sdk.utils.parse_legacy_btl_extended_log", "modulename": "golem_base_sdk.utils", "qualname": "parse_legacy_btl_extended_log", "kind": "function", "doc": "

For legacy extend ABI types, the type signature in the ABI does\nnot correspond to the actual data returned, so we need\nto parse the data ourselves.

\n", "signature": "(\tlog_receipt: web3.types.LogReceipt) -> golem_base_sdk.types.ExtendEntityReturnType:", "funcdef": "def"}]; + /** pdoc search index */const docs = [{"fullname": "golem_base_sdk", "modulename": "golem_base_sdk", "kind": "module", "doc": "

GolemBase Python SDK

\n"}, {"fullname": "golem_base_sdk.GolemBaseClient", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient", "kind": "class", "doc": "

The Golem Base client used to interact with Golem Base.\nMany useful methods are implemented directly on this type, while more\ngeneric ethereum methods can be accessed through the underlying\nweb3 client that you can access with the\nGolemBaseClient.http_client()\nmethod.

\n"}, {"fullname": "golem_base_sdk.GolemBaseClient.__init__", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.__init__", "kind": "function", "doc": "

\n", "signature": "(rpc_url: str, ws_client: web3.main.AsyncWeb3, private_key: bytes)"}, {"fullname": "golem_base_sdk.GolemBaseClient.create", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.create", "kind": "function", "doc": "

Static method to create a GolemBaseClient instance,\nthis is the preferred method to create an instance.

\n", "signature": "(\trpc_url: str,\tws_url: str,\tprivate_key: bytes) -> golem_base_sdk.GolemBaseClient:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.account", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.account", "kind": "variable", "doc": "

\n"}, {"fullname": "golem_base_sdk.GolemBaseClient.golem_base_contract", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.golem_base_contract", "kind": "variable", "doc": "

\n"}, {"fullname": "golem_base_sdk.GolemBaseClient.http_client", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.http_client", "kind": "function", "doc": "

Get the underlying web3 http client

\n", "signature": "(self):", "funcdef": "def"}, {"fullname": "golem_base_sdk.GolemBaseClient.ws_client", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.ws_client", "kind": "function", "doc": "

Get the underlying web3 websocket client

\n", "signature": "(self) -> web3.main.AsyncWeb3:", "funcdef": "def"}, {"fullname": "golem_base_sdk.GolemBaseClient.is_connected", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.is_connected", "kind": "function", "doc": "

Check whether the client's underlying http client is connected

\n", "signature": "(self) -> bool:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.disconnect", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.disconnect", "kind": "function", "doc": "

Disconnect both the underlying http and ws clients and\nunsubscribe from all subscriptions

\n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_account_address", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_account_address", "kind": "function", "doc": "

Get the address associated with the private key that this client\nwas created with

\n", "signature": "(self) -> eth_typing.evm.ChecksumAddress:", "funcdef": "def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_storage_value", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_storage_value", "kind": "function", "doc": "

Get the storage value stored in the given entity

\n", "signature": "(self, entity_key: golem_base_sdk.types.EntityKey) -> bytes:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_entity_metadata", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_entity_metadata", "kind": "function", "doc": "

Get the metadata of the given entity

\n", "signature": "(\tself,\tentity_key: golem_base_sdk.types.EntityKey) -> golem_base_sdk.types.EntityMetadata:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_entities_to_expire_at_block", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_entities_to_expire_at_block", "kind": "function", "doc": "

Get all entities that will expire at the given block

\n", "signature": "(self, block_number: int) -> Sequence[golem_base_sdk.types.EntityKey]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_entity_count", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_entity_count", "kind": "function", "doc": "

Get the total entity count in Golem Base

\n", "signature": "(self) -> int:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_all_entity_keys", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_all_entity_keys", "kind": "function", "doc": "

Get all entity keys in Golem Base

\n", "signature": "(self) -> Sequence[golem_base_sdk.types.EntityKey]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.get_entities_of_owner", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.get_entities_of_owner", "kind": "function", "doc": "

Get all the entities owned by the given address

\n", "signature": "(\tself,\towner: eth_typing.evm.ChecksumAddress) -> Sequence[golem_base_sdk.types.EntityKey]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.query_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.query_entities", "kind": "function", "doc": "

Get all entities that satisfy the given Golem Base query

\n", "signature": "(self, query: str) -> Sequence[golem_base_sdk.types.QueryEntitiesResult]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.create_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.create_entities", "kind": "function", "doc": "

Create entities in Golem Base

\n", "signature": "(\tself,\tcreates: Sequence[golem_base_sdk.types.GolemBaseCreate]) -> Sequence[golem_base_sdk.types.CreateEntityReturnType]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.update_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.update_entities", "kind": "function", "doc": "

Update entities in Golem Base

\n", "signature": "(\tself,\tupdates: Sequence[golem_base_sdk.types.GolemBaseUpdate]) -> Sequence[golem_base_sdk.types.UpdateEntityReturnType]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.delete_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.delete_entities", "kind": "function", "doc": "

Delete entities from Golem Base

\n", "signature": "(\tself,\tdeletes: Sequence[golem_base_sdk.types.GolemBaseDelete]) -> Sequence[golem_base_sdk.types.EntityKey]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.extend_entities", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.extend_entities", "kind": "function", "doc": "

Extend the BTL of entities in Golem Base

\n", "signature": "(\tself,\textensions: Sequence[golem_base_sdk.types.GolemBaseExtend]) -> Sequence[golem_base_sdk.types.ExtendEntityReturnType]:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.send_transaction", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.send_transaction", "kind": "function", "doc": "

Send a generic transaction to Golem Base.\nThis transaction can contain multiple create, update, delete and\nextend operations

\n", "signature": "(\tself,\tcreates: Optional[Sequence[golem_base_sdk.types.GolemBaseCreate]] = None,\tupdates: Optional[Sequence[golem_base_sdk.types.GolemBaseUpdate]] = None,\tdeletes: Optional[Sequence[golem_base_sdk.types.GolemBaseDelete]] = None,\textensions: Optional[Sequence[golem_base_sdk.types.GolemBaseExtend]] = None) -> golem_base_sdk.types.GolemBaseTransactionReceipt:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.GolemBaseClient.watch_logs", "modulename": "golem_base_sdk", "qualname": "GolemBaseClient.watch_logs", "kind": "function", "doc": "

Subscribe to events on Golem Base.\nYou can pass in four different callbacks, and the right one will\nbe invoked for every create, update, delete, and extend operation.

\n", "signature": "(\tself,\tcreate_callback: Callable[[golem_base_sdk.types.CreateEntityReturnType], NoneType],\tupdate_callback: Callable[[golem_base_sdk.types.UpdateEntityReturnType], NoneType],\tdelete_callback: Callable[[golem_base_sdk.types.EntityKey], NoneType],\textend_callback: Callable[[golem_base_sdk.types.ExtendEntityReturnType], NoneType]) -> None:", "funcdef": "async def"}, {"fullname": "golem_base_sdk.constants", "modulename": "golem_base_sdk.constants", "kind": "module", "doc": "

Constants used in the Golem Base SDK

\n"}, {"fullname": "golem_base_sdk.constants.STORAGE_ADDRESS", "modulename": "golem_base_sdk.constants", "qualname": "STORAGE_ADDRESS", "kind": "variable", "doc": "

\n", "annotation": ": Final[golem_base_sdk.types.Address]", "default_value": "GenericBytes(0x0000000000000000000000000000000060138453)"}, {"fullname": "golem_base_sdk.constants.GOLEM_BASE_ABI", "modulename": "golem_base_sdk.constants", "qualname": "GOLEM_BASE_ABI", "kind": "variable", "doc": "

\n", "annotation": ": Final[Sequence[dict[str, Any]]]", "default_value": "[{'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityCreated', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityUpdated', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityDeleted', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'oldExpirationBlock', 'type': 'uint256'}, {'indexed': False, 'name': 'newExpirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityBTLExtended', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityBTLExptended', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'entityKey', 'type': 'uint256'}, {'indexed': False, 'name': 'expirationBlock', 'type': 'uint256'}], 'name': 'GolemBaseStorageEntityTTLExptended', 'type': 'event'}]"}, {"fullname": "golem_base_sdk.types", "modulename": "golem_base_sdk.types", "kind": "module", "doc": "

Golem Base SDK Types

\n"}, {"fullname": "golem_base_sdk.types.GenericBytes", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes", "kind": "class", "doc": "

Class to represent bytes that can be converted to more meaningful types

\n"}, {"fullname": "golem_base_sdk.types.GenericBytes.__init__", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.__init__", "kind": "function", "doc": "

\n", "signature": "(generic_bytes: bytes)"}, {"fullname": "golem_base_sdk.types.GenericBytes.generic_bytes", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.generic_bytes", "kind": "variable", "doc": "

\n", "annotation": ": bytes"}, {"fullname": "golem_base_sdk.types.GenericBytes.as_hex_string", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.as_hex_string", "kind": "function", "doc": "

Convert this instance to a hexadecimal string

\n", "signature": "(self) -> eth_typing.encoding.HexStr:", "funcdef": "def"}, {"fullname": "golem_base_sdk.types.GenericBytes.as_address", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.as_address", "kind": "function", "doc": "

Convert this instance to a eth_typing.ChecksumAddress

\n", "signature": "(self) -> eth_typing.evm.ChecksumAddress:", "funcdef": "def"}, {"fullname": "golem_base_sdk.types.GenericBytes.from_hex_string", "modulename": "golem_base_sdk.types", "qualname": "GenericBytes.from_hex_string", "kind": "function", "doc": "

Create a GenericBytes instance from a hexadecimal string

\n", "signature": "(hexstr: str) -> golem_base_sdk.types.GenericBytes:", "funcdef": "def"}, {"fullname": "golem_base_sdk.types.EntityKey", "modulename": "golem_base_sdk.types", "qualname": "EntityKey", "kind": "variable", "doc": "

\n", "default_value": "golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.Address", "modulename": "golem_base_sdk.types", "qualname": "Address", "kind": "variable", "doc": "

\n", "default_value": "golem_base_sdk.types.Address"}, {"fullname": "golem_base_sdk.types.Annotation", "modulename": "golem_base_sdk.types", "qualname": "Annotation", "kind": "class", "doc": "

Class to represent generic annotations

\n", "bases": "typing.Generic[V]"}, {"fullname": "golem_base_sdk.types.Annotation.__init__", "modulename": "golem_base_sdk.types", "qualname": "Annotation.__init__", "kind": "function", "doc": "

\n", "signature": "(key: str, value: V)"}, {"fullname": "golem_base_sdk.types.Annotation.key", "modulename": "golem_base_sdk.types", "qualname": "Annotation.key", "kind": "variable", "doc": "

\n", "annotation": ": str"}, {"fullname": "golem_base_sdk.types.Annotation.value", "modulename": "golem_base_sdk.types", "qualname": "Annotation.value", "kind": "variable", "doc": "

\n", "annotation": ": V"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate", "kind": "class", "doc": "

Class to represent a create operation in Golem Base

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.__init__", "kind": "function", "doc": "

\n", "signature": "(\tdata: bytes,\tttl: int,\tstring_annotations: Sequence[golem_base_sdk.types.Annotation[str]],\tnumeric_annotations: Sequence[golem_base_sdk.types.Annotation[int]])"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.data", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.data", "kind": "variable", "doc": "

\n", "annotation": ": bytes"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.ttl", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.ttl", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.string_annotations", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.string_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[str]]"}, {"fullname": "golem_base_sdk.types.GolemBaseCreate.numeric_annotations", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseCreate.numeric_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[int]]"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate", "kind": "class", "doc": "

Class to represent an update operation in Golem Base

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.__init__", "kind": "function", "doc": "

\n", "signature": "(\tentity_key: golem_base_sdk.types.EntityKey,\tdata: bytes,\tttl: int,\tstring_annotations: Sequence[golem_base_sdk.types.Annotation[str]],\tnumeric_annotations: Sequence[golem_base_sdk.types.Annotation[int]])"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.entity_key", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.data", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.data", "kind": "variable", "doc": "

\n", "annotation": ": bytes"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.ttl", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.ttl", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.string_annotations", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.string_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[str]]"}, {"fullname": "golem_base_sdk.types.GolemBaseUpdate.numeric_annotations", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseUpdate.numeric_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[int]]"}, {"fullname": "golem_base_sdk.types.GolemBaseDelete", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseDelete", "kind": "class", "doc": "

Class to represent a delete operation in Golem Base

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseDelete.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseDelete.__init__", "kind": "function", "doc": "

\n", "signature": "(entity_key: golem_base_sdk.types.EntityKey)"}, {"fullname": "golem_base_sdk.types.GolemBaseDelete.entity_key", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseDelete.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.GolemBaseExtend", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseExtend", "kind": "class", "doc": "

Class to represent a BTL extend operation in Golem Base

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseExtend.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseExtend.__init__", "kind": "function", "doc": "

\n", "signature": "(entity_key: golem_base_sdk.types.EntityKey, number_of_blocks: int)"}, {"fullname": "golem_base_sdk.types.GolemBaseExtend.entity_key", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseExtend.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.GolemBaseExtend.number_of_blocks", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseExtend.number_of_blocks", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction", "kind": "class", "doc": "

Class to represent a transaction in Golem Base.\nA transaction consist of one or more\nGolemBaseCreate,\nGolemBaseUpdate,\nGolemBaseDelete and\nGolemBaseExtend\noperations.

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.__init__", "kind": "function", "doc": "

\n", "signature": "(\tcreates: Optional[Sequence[golem_base_sdk.types.GolemBaseCreate]] = None,\tupdates: Optional[Sequence[golem_base_sdk.types.GolemBaseUpdate]] = None,\tdeletes: Optional[Sequence[golem_base_sdk.types.GolemBaseDelete]] = None,\textensions: Optional[Sequence[golem_base_sdk.types.GolemBaseExtend]] = None)"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.creates", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.creates", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.GolemBaseCreate]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.updates", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.updates", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.GolemBaseUpdate]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.deletes", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.deletes", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.GolemBaseDelete]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransaction.extensions", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransaction.extensions", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.GolemBaseExtend]"}, {"fullname": "golem_base_sdk.types.CreateEntityReturnType", "modulename": "golem_base_sdk.types", "qualname": "CreateEntityReturnType", "kind": "class", "doc": "

The return type of a Golem Base create operation

\n"}, {"fullname": "golem_base_sdk.types.CreateEntityReturnType.__init__", "modulename": "golem_base_sdk.types", "qualname": "CreateEntityReturnType.__init__", "kind": "function", "doc": "

\n", "signature": "(expiration_block: int, entity_key: golem_base_sdk.types.EntityKey)"}, {"fullname": "golem_base_sdk.types.CreateEntityReturnType.expiration_block", "modulename": "golem_base_sdk.types", "qualname": "CreateEntityReturnType.expiration_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.CreateEntityReturnType.entity_key", "modulename": "golem_base_sdk.types", "qualname": "CreateEntityReturnType.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.UpdateEntityReturnType", "modulename": "golem_base_sdk.types", "qualname": "UpdateEntityReturnType", "kind": "class", "doc": "

The return type of a Golem Base update operation

\n"}, {"fullname": "golem_base_sdk.types.UpdateEntityReturnType.__init__", "modulename": "golem_base_sdk.types", "qualname": "UpdateEntityReturnType.__init__", "kind": "function", "doc": "

\n", "signature": "(expiration_block: int, entity_key: golem_base_sdk.types.EntityKey)"}, {"fullname": "golem_base_sdk.types.UpdateEntityReturnType.expiration_block", "modulename": "golem_base_sdk.types", "qualname": "UpdateEntityReturnType.expiration_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.UpdateEntityReturnType.entity_key", "modulename": "golem_base_sdk.types", "qualname": "UpdateEntityReturnType.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType", "kind": "class", "doc": "

The return type of a Golem Base extend operation

\n"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType.__init__", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType.__init__", "kind": "function", "doc": "

\n", "signature": "(\told_expiration_block: int,\tnew_expiration_block: int,\tentity_key: golem_base_sdk.types.EntityKey)"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType.old_expiration_block", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType.old_expiration_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType.new_expiration_block", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType.new_expiration_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.ExtendEntityReturnType.entity_key", "modulename": "golem_base_sdk.types", "qualname": "ExtendEntityReturnType.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt", "kind": "class", "doc": "

The return type of a Golem Base transaction

\n"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.__init__", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.__init__", "kind": "function", "doc": "

\n", "signature": "(\tcreates: Sequence[golem_base_sdk.types.CreateEntityReturnType],\tupdates: Sequence[golem_base_sdk.types.UpdateEntityReturnType],\textensions: Sequence[golem_base_sdk.types.ExtendEntityReturnType],\tdeletes: Sequence[golem_base_sdk.types.EntityKey])"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.creates", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.creates", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.CreateEntityReturnType]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.updates", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.updates", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.UpdateEntityReturnType]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.extensions", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.extensions", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.ExtendEntityReturnType]"}, {"fullname": "golem_base_sdk.types.GolemBaseTransactionReceipt.deletes", "modulename": "golem_base_sdk.types", "qualname": "GolemBaseTransactionReceipt.deletes", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.EntityKey]"}, {"fullname": "golem_base_sdk.types.EntityMetadata", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata", "kind": "class", "doc": "

A class representing entity metadata

\n"}, {"fullname": "golem_base_sdk.types.EntityMetadata.__init__", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.__init__", "kind": "function", "doc": "

\n", "signature": "(\tentity_key: golem_base_sdk.types.EntityKey,\towner: golem_base_sdk.types.Address,\texpires_at_block: int,\tstring_annotations: Sequence[golem_base_sdk.types.Annotation[str]],\tnumeric_annotations: Sequence[golem_base_sdk.types.Annotation[int]])"}, {"fullname": "golem_base_sdk.types.EntityMetadata.entity_key", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.EntityMetadata.owner", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.owner", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.Address"}, {"fullname": "golem_base_sdk.types.EntityMetadata.expires_at_block", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.expires_at_block", "kind": "variable", "doc": "

\n", "annotation": ": int"}, {"fullname": "golem_base_sdk.types.EntityMetadata.string_annotations", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.string_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[str]]"}, {"fullname": "golem_base_sdk.types.EntityMetadata.numeric_annotations", "modulename": "golem_base_sdk.types", "qualname": "EntityMetadata.numeric_annotations", "kind": "variable", "doc": "

\n", "annotation": ": Sequence[golem_base_sdk.types.Annotation[int]]"}, {"fullname": "golem_base_sdk.types.QueryEntitiesResult", "modulename": "golem_base_sdk.types", "qualname": "QueryEntitiesResult", "kind": "class", "doc": "

A class representing the return value of a Golem Base query

\n"}, {"fullname": "golem_base_sdk.types.QueryEntitiesResult.__init__", "modulename": "golem_base_sdk.types", "qualname": "QueryEntitiesResult.__init__", "kind": "function", "doc": "

\n", "signature": "(entity_key: golem_base_sdk.types.EntityKey, storage_value: bytes)"}, {"fullname": "golem_base_sdk.types.QueryEntitiesResult.entity_key", "modulename": "golem_base_sdk.types", "qualname": "QueryEntitiesResult.entity_key", "kind": "variable", "doc": "

\n", "annotation": ": golem_base_sdk.types.EntityKey"}, {"fullname": "golem_base_sdk.types.QueryEntitiesResult.storage_value", "modulename": "golem_base_sdk.types", "qualname": "QueryEntitiesResult.storage_value", "kind": "variable", "doc": "

\n", "annotation": ": bytes"}, {"fullname": "golem_base_sdk.utils", "modulename": "golem_base_sdk.utils", "kind": "module", "doc": "

Utility methods

\n"}, {"fullname": "golem_base_sdk.utils.rlp_encode_transaction", "modulename": "golem_base_sdk.utils", "qualname": "rlp_encode_transaction", "kind": "function", "doc": "

Encode a Golem Base transaction in RLP

\n", "signature": "(tx: golem_base_sdk.types.GolemBaseTransaction) -> bytes:", "funcdef": "def"}, {"fullname": "golem_base_sdk.utils.parse_legacy_btl_extended_log", "modulename": "golem_base_sdk.utils", "qualname": "parse_legacy_btl_extended_log", "kind": "function", "doc": "

For legacy extend ABI types, the type signature in the ABI does\nnot correspond to the actual data returned, so we need\nto parse the data ourselves.

\n", "signature": "(\tlog_receipt: web3.types.LogReceipt) -> golem_base_sdk.types.ExtendEntityReturnType:", "funcdef": "def"}]; // mirrored in build-search-index.js (part 1) // Also split on html tags. this is a cheap heuristic, but good enough.