polkascan / py-substrate-interface

Python Substrate Interface
https://polkascan.github.io/py-substrate-interface/
Apache License 2.0
240 stars 114 forks source link

How to pass multiple parameters using the query method #169

Closed wowvwow closed 2 years ago

wowvwow commented 2 years ago

I need to pass in these two types of parameters (u64, AccountId32), if a's type is u64, b's type is AccountId32, params is a list, so how to write it?

image

substrate.query( module='xxxx', storage_function='xxxx', params=[a, b],)

Traceback (most recent call last): File "/usr/local/lib/python3.8/dist-packages/substrateinterface/base.py", line 905, in generate_storage_hash param_hasher = hashers[idx] IndexError: list index out of range

During handling of the above exception, another exception occurred:

Traceback (most recent call last): user_staker_info = self.substrate.query( File "/usr/local/lib/python3.8/dist-packages/substrateinterface/base.py", line 1279, in query storage_hash = self.generate_storage_hash( File "/usr/local/lib/python3.8/dist-packages/substrateinterface/base.py", line 907, in generate_storage_hash raise ValueError(f'No hasher found for param #{idx + 1}') ValueError: No hasher found for param #2

arjanz commented 2 years ago

Confirmed, the library can't handle storage functions that has 1 param and that param is a tuple of multiple items (in this case (u64, AccountId32)).

Information about storage function (Khala network):

storage_function = substrate.get_metadata_storage_function("PhalaStakePool", "PoolStakers")
# {'name': 'PoolStakers', 'modifier': 'Optional', 'type': {'Map': {'hashers': ['Twox64Concat'], 'key': 350, 'value': 351}}, 'default': '\x00', 'documentation': [' Mapping from (pid, staker) to UserStakeInfo']}

Type information about param 1:

350: {'id': 350, 'type': {'path': [], 'params': [], 'def': {'tuple': [8, 0]}, 'docs': []}}

# Elements of tuple
{'id': 8, 'type': {'path': [], 'params': [], 'def': {'primitive': 'u64'}, 'docs': []}}
{'id': 0, 'type': {'path': ['sp_core', 'crypto', 'AccountId32'], 'params': [], 'def': {'composite': {'fields': [{'name': None, 'type': 1, 'typeName': '[u8; 32]', 'docs': []}]}}, 'docs': []}}

Correct call would be:

result = substrate.query("PhalaStakePool", "PoolStakers", params=[(975, "44sZ3cudfTtY8Xz6urk6nSij2KpxZa1uJ5Huf3eGRsNtJ9au")])

Desired result would be that the tuple would be treated as 1 param and hashed as a whole with the hasher as defined in the metadata.

Result now:

Exception: ValueError: Storage function requires 2 parameters, 1 given

arjanz commented 2 years ago

A fix has been released in https://github.com/polkascan/py-substrate-interface/releases/tag/v1.1.6

This has been a tricky one because although the key is a tuple, this storage function has only one hasher so can have only one parameter.

So the correct way to query this storage function:

result = substrate.query("PhalaStakePool", "PoolStakers", params=[(975, "44sZ3cudfTtY8Xz6urk6nSij2KpxZa1uJ5Huf3eGRsNtJ9au")])
result_map = substrate.query_map("PhalaStakePool", "PoolStakers")

Other examples:

result2 = substrate.query("PhalaMq", "OffchainIngress", params=[{"Worker": "0xc47c8b555ac30b148fe5894dd966cef64b3a09c7c0c7de221f5b969036159b45"}])
result3 = substrate.query("PhalaRegistry", "Workers", params=["0x7ab53a9070cb399483fbb4d43ef37bfd0da65c9d1b871755c39103f73845cf51"])
result4 = substrate.query("Staking", "ErasStakers", params=[569, "1zugcag7cJVBtVRnFxv5Qftn7xKAnR6YJ9x4x3XLgGgmNnS"])
wowvwow commented 2 years ago

ok, ty u, I checked it, and It works great