polkascan/py-scale-codec

Getting KeyError: '0000' when attempting to decode events vector

Closed this issue · 2 comments

What I'm trying to do?

Decode events after making RPC call to state_getStorageAt

What's happening?

Getting KeyError: '0000' at the time of decoding.

Something to help reproduce.

result = "0x64000000000........ ........" # This is the output of `state_getStorageAt` call, which is correct. I verified it on https://polkadot.js.org/apps/

# Loaded the necessary types before this as I learned from README
events = runtime_config.create_scale_object(
    "Vec<EventRecord<Event, Hash>>", ScaleBytes(result), metadata=metadata,
)
print(events.decode())

Here's the full traceback

../../../.pyenv/versions/3.8.1/envs/substrateutils/lib/python3.8/site-packages/scalecodec/base.py:660: in decode
    self.value_serialized = self.process()
../../../.pyenv/versions/3.8.1/envs/substrateutils/lib/python3.8/site-packages/scalecodec/types.py:787: in process
    element = self.process_type(self.sub_type, metadata=self.metadata)
../../../.pyenv/versions/3.8.1/envs/substrateutils/lib/python3.8/site-packages/scalecodec/base.py:743: in process_type
    obj.decode(check_remaining=False)
../../../.pyenv/versions/3.8.1/envs/substrateutils/lib/python3.8/site-packages/scalecodec/base.py:660: in decode
    self.value_serialized = self.process()
../../../.pyenv/versions/3.8.1/envs/substrateutils/lib/python3.8/site-packages/scalecodec/types.py:2504: in process
    value = super().process()
../../../.pyenv/versions/3.8.1/envs/substrateutils/lib/python3.8/site-packages/scalecodec/types.py:462: in process
    field_obj = self.process_type(data_type, metadata=self.metadata)
../../../.pyenv/versions/3.8.1/envs/substrateutils/lib/python3.8/site-packages/scalecodec/base.py:743: in process_type
    obj.decode(check_remaining=False)
../../../.pyenv/versions/3.8.1/envs/substrateutils/lib/python3.8/site-packages/scalecodec/base.py:660: in decode
    self.value_serialized = self.process()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <GenericEvent(value=None)>

    def process(self):
    
        self.event_index = self.get_next_bytes(2).hex()
    
        # Decode attributes
>       self.event_module = self.metadata.event_index[self.event_index][0]
E       KeyError: '0000'

../../../.pyenv/versions/3.8.1/envs/substrateutils/lib/python3.8/site-packages/scalecodec/types.py:2432: KeyError

The type lookup also changed with MetadataV14. Before V14 there were string type references the libraries had to map manually in a 'type registry', like Vec<EventRecord<Event, Hash>>. Since V14 however these reference are now integer identifiers pointed to the embedded type registry.

I will explain step for step:

First of all, you cannot rely on a fixed type string, although the type decomposition might be the same, the unique identifier can differ per runtime because of the position in the embedded type registry. So first of all you have to retrieve the System.Events storage function from the metadata:

system_pallet = [p for p in metadata.pallets if p['name'] == 'System'][0]
event_storage_function = [s for s in system_pallet['storage']['entries'] if s['name'] == "Events"][0]

In this case you see that the return type event_storage_function['type'] is a Plain type (no param arguments required) with id 18.

So you will decode the data returned from the RPC with embedded type 18. The following helper function will return the type string used internally in the library (which is also backwards compatible with metadata prior to V14):

event_storage_function.get_value_type_string()

So to put this all together you can do something like:

event_data = "0x2000000000000000b0338609000000000200000001000000000080b2e60e0000000002000000020000000003be1957935299d0be2f35b8856751feab95fc7089239366b52b72ca98249b94300000020000000500be1957935299d0be2f35b8856751feab95fc7089239366b52b72ca98249b943000264d2823000000000000000000000000000200000005027a9650a6bd43f1e0b4546affb88f8c14213e1fb60512692c2b39fbfcfc56b703be1957935299d0be2f35b8856751feab95fc7089239366b52b72ca98249b943000264d2823000000000000000000000000000200000013060c4c700700000000000000000000000000000200000005047b8441d5110c178c29be709793a41d73ae8b3119a971b18fbd20945ea5d622f00313dc01000000000000000000000000000002000000000010016b0b00000000000000"

system_pallet = [p for p in metadata.pallets if p['name'] == 'System'][0]
event_storage_function = [s for s in system_pallet['storage']['entries'] if s['name'] == "Events"][0]


event = runtime_config.create_scale_object(
    event_storage_function.get_value_type_string(), data=ScaleBytes(event_data), metadata=metadata
)
print(event.decode())

Thank you @arjanz, appreciate it!

Your suggestions helped and I was able to fix the issue. Apologies, I missed updating here.