├── docs ├── reference │ ├── base.md │ ├── types.md │ ├── utils │ │ ├── math.md │ │ └── ss58.md │ └── type_registry.md ├── examples.md └── index.md ├── scalecodec ├── constants.py ├── type_registry │ ├── contracts-on-rococo.json │ ├── moonbeam.json │ ├── moonbase-alpha.json │ ├── statemine.json │ ├── statemint.json │ ├── acala.json │ ├── substrate-node-template.json │ ├── rococo.json │ ├── moonriver.json │ ├── __init__.py │ ├── canvas.json │ ├── test.json │ ├── westend.json │ ├── polkadot.json │ ├── crust.json │ ├── kusama.json │ └── karura.json ├── utils │ ├── __init__.py │ ├── math.py │ └── ss58.py ├── __init__.py ├── exceptions.py └── updater.py ├── requirements.txt ├── test ├── fixtures │ └── scale_info_defaults.json ├── __init__.py ├── test_runtime_call.py ├── test_scalebytes.py ├── test_runtime_configuration.py ├── test_metadata.py ├── test_ss58.py ├── test_scale_info.py └── test_type_encoding.py ├── .github └── workflows │ ├── dependency-review.yml │ ├── unittests.yml │ └── deploypypi.yml ├── .gitignore ├── mkdocs.yml ├── setup.py ├── LICENSE └── README.md /docs/reference/base.md: -------------------------------------------------------------------------------- 1 | ::: scalecodec.base 2 | -------------------------------------------------------------------------------- /docs/reference/types.md: -------------------------------------------------------------------------------- 1 | ::: scalecodec.types 2 | -------------------------------------------------------------------------------- /docs/reference/utils/math.md: -------------------------------------------------------------------------------- 1 | ::: scalecodec.utils.math 2 | -------------------------------------------------------------------------------- /docs/reference/utils/ss58.md: -------------------------------------------------------------------------------- 1 | ::: scalecodec.utils.ss58 2 | -------------------------------------------------------------------------------- /scalecodec/constants.py: -------------------------------------------------------------------------------- 1 | TYPE_DECOMP_MAX_RECURSIVE = 9 2 | -------------------------------------------------------------------------------- /docs/reference/type_registry.md: -------------------------------------------------------------------------------- 1 | ::: scalecodec.type_registry 2 | -------------------------------------------------------------------------------- /scalecodec/type_registry/contracts-on-rococo.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": { 3 | "ContractExecResult": "ContractExecResultTo269" 4 | }, 5 | "versioning": [] 6 | } 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | base58==2.0.1 2 | atomicwrites~=1.4.0 3 | attrs~=20.3.0 4 | coverage==5.3 5 | more-itertools~=8.6.0 6 | pluggy==0.13.1 7 | pytest>=6.1.2 8 | requests>=2.24.0 9 | six==1.15.0 10 | 11 | mkdocs 12 | mkdocs-material 13 | mkdocs-autorefs 14 | mkdocstrings 15 | mkdocstrings[python] 16 | -------------------------------------------------------------------------------- /scalecodec/type_registry/moonbeam.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 900, 3 | "types": { 4 | "EthereumAddress": "H160", 5 | "Address": "EthereumAddress", 6 | "LookupSource": "EthereumAddress", 7 | "AccountId": "EthereumAddress", 8 | "ExtrinsicSignature": "EcdsaSignature" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scalecodec/type_registry/moonbase-alpha.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 901, 3 | "types": { 4 | "EthereumAddress": "H160", 5 | "Address": "EthereumAddress", 6 | "LookupSource": "EthereumAddress", 7 | "AccountId": "EthereumAddress", 8 | "ExtrinsicSignature": "EcdsaSignature" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scalecodec/type_registry/statemine.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 1, 3 | "types": { 4 | "TAssetBalance": "u128", 5 | "ProxyType": { 6 | "type": "enum", 7 | "value_list": [ 8 | "Any", 9 | "NonTransfer", 10 | "CancelProxy", 11 | "Assets", 12 | "AssetOwner", 13 | "AssetManager", 14 | "Staking" 15 | ] 16 | } 17 | }, 18 | "versioning": [ 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /scalecodec/type_registry/statemint.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 1, 3 | "types": { 4 | "TAssetBalance": "u128", 5 | "ProxyType": { 6 | "type": "enum", 7 | "value_list": [ 8 | "Any", 9 | "NonTransfer", 10 | "CancelProxy", 11 | "Assets", 12 | "AssetOwner", 13 | "AssetManager", 14 | "Staking" 15 | ] 16 | } 17 | }, 18 | "versioning": [ 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /scalecodec/type_registry/acala.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": { 3 | "MultiSignature": { 4 | "type": "enum", 5 | "type_mapping": [ 6 | [ 7 | "Ed25519", 8 | "Ed25519Signature" 9 | ], 10 | [ 11 | "Sr25519", 12 | "Sr25519Signature" 13 | ], 14 | [ 15 | "Ecdsa", 16 | "EcdsaSignature" 17 | ], 18 | [ 19 | "Ethereum", 20 | "[u8; 65]" 21 | ], 22 | [ 23 | "Eip712", 24 | "[u8; 65]" 25 | ] 26 | ] 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/fixtures/scale_info_defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": { 3 | "str": "Bytes", 4 | "Compact": "CompactMoment", 5 | "sp_core::crypto::AccountId32": "GenericAccountId", 6 | "!!!!!sp_core::crypto::GenericConsensusEngineId": "GenericConsensusEngineId", 7 | "frame_support::storage::weak_bounded_vec::WeakBoundedVec": "BoundedVec", 8 | "frame_support::storage::bounded_vec::BoundedVec": "BoundedVec", 9 | "primitive_types::H256": "H256", 10 | "ContractExecResultTo260": { 11 | "type": "enum", 12 | "base_class": "GenericContractExecResult" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | -------------------------------------------------------------------------------- /scalecodec/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | -------------------------------------------------------------------------------- /scalecodec/__init__.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Import all type to make sure types classes are registered when RuntimeConfiguration inits. 18 | from .types import * 19 | -------------------------------------------------------------------------------- /scalecodec/exceptions.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | class RemainingScaleBytesNotEmptyException(Exception): 19 | pass 20 | 21 | 22 | class InvalidScaleTypeValueException(Exception): 23 | pass 24 | 25 | 26 | class MetadataCallFunctionNotFound(ValueError): 27 | pass 28 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. 4 | # 5 | # Source repository: https://github.com/actions/dependency-review-action 6 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 7 | name: 'Dependency Review' 8 | on: [pull_request] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | dependency-review: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: 'Checkout Repository' 18 | uses: actions/checkout@v3 19 | - name: 'Dependency Review' 20 | uses: actions/dependency-review-action@v2 21 | -------------------------------------------------------------------------------- /scalecodec/type_registry/substrate-node-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 100, 3 | "types": { 4 | "Address": "MultiAddress", 5 | "LookupSource": "MultiAddress", 6 | "AccountInfo": "AccountInfoWithTripleRefCount", 7 | "Keys": { 8 | "type": "struct", 9 | "type_mapping": [ 10 | ["grandpa", "AccountId"], 11 | ["babe", "AccountId"], 12 | ["im_online", "AccountId"], 13 | ["authority_discovery", "AccountId"], 14 | ["parachains", "AccountId"] 15 | ] 16 | } 17 | }, 18 | "versioning": [ 19 | { 20 | "runtime_range": [0, 99], 21 | "types": { 22 | "Address": "AccountIdAddress", 23 | "LookupSource": "AccountIdAddress", 24 | "AccountInfo": "AccountInfoWithRefCount" 25 | } 26 | }, 27 | { 28 | "runtime_range": [100, null], 29 | "types": { 30 | "Address": "MultiAddress", 31 | "LookupSource": "MultiAddress", 32 | "AccountInfo": "AccountInfoWithTripleRefCount" 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/unittests.yml: -------------------------------------------------------------------------------- 1 | name: Run unit tests 2 | 3 | on: 4 | push: 5 | branches: [master, develop, v1.0] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master, develop, v1.0] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | python-version: ['3.10', '3.11', '3.12'] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v1 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install -r requirements.txt 28 | - name: Lint with flake8 29 | run: | 30 | pip install flake8 31 | # stop the build if there are Python syntax errors or undefined names 32 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 33 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 34 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 35 | - name: Test with pytest 36 | run: | 37 | pip install pytest 38 | pytest 39 | -------------------------------------------------------------------------------- /.github/workflows/deploypypi.yml: -------------------------------------------------------------------------------- 1 | name: Test and deploy to PYPI 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Python 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: '3.10' 16 | - name: Install project dependencies 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install -r requirements.txt 20 | - name: Lint with flake8 21 | run: | 22 | pip install flake8 23 | # stop the build if there are Python syntax errors or undefined names 24 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 25 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 26 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 27 | - name: Test with pytest 28 | run: | 29 | pip install pytest 30 | pytest 31 | - name: Install deploy dependencies 32 | run: | 33 | pip install setuptools wheel twine 34 | - name: Build and publish 35 | env: 36 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 37 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 38 | run: | 39 | python setup.py sdist bdist_wheel 40 | twine upload dist/* 41 | -------------------------------------------------------------------------------- /test/test_runtime_call.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from scalecodec.base import RuntimeConfigurationObject 4 | from scalecodec.type_registry import load_type_registry_preset 5 | 6 | 7 | class RuntimeCallTestCase(unittest.TestCase): 8 | 9 | runtime_config: RuntimeConfigurationObject 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | cls.runtime_config = RuntimeConfigurationObject() 14 | cls.runtime_config.clear_type_registry() 15 | cls.runtime_config.update_type_registry(load_type_registry_preset("core")) 16 | 17 | def test_encode_runtime_calls(self): 18 | for api, methods in self.runtime_config.type_registry["runtime_api"].items(): 19 | 20 | runtime_api_types = self.runtime_config.type_registry["runtime_api"][api].get("types", {}) 21 | # Add runtime API types to registry 22 | self.runtime_config.update_type_registry_types(runtime_api_types) 23 | 24 | for method, runtime_call in methods["methods"].items(): 25 | runtime_call['api'] = api 26 | runtime_call['method'] = method 27 | 28 | runtime_call_obj = self.runtime_config.create_scale_object("RuntimeCallDefinition") 29 | runtime_call_obj.encode(runtime_call) 30 | 31 | self.assertEqual(runtime_call_obj.value['method'], method) 32 | self.assertIn('params', runtime_call_obj.value) 33 | 34 | -------------------------------------------------------------------------------- /scalecodec/utils/math.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # math.py 18 | 19 | """Some simple math-related utility functions not present in the standard 20 | library. 21 | """ 22 | 23 | from math import ceil, log2 24 | 25 | 26 | def trailing_zeros(value: int) -> int: 27 | """Returns the number of trailing zeros in the binary representation of 28 | the given integer. 29 | """ 30 | num_zeros = 0 31 | while value & 1 == 0: 32 | num_zeros += 1 33 | value >>= 1 34 | return num_zeros 35 | 36 | 37 | def next_power_of_two(value: int) -> int: 38 | """Returns the smallest power of two that is greater than or equal 39 | to the given integer. 40 | """ 41 | if value < 0: 42 | raise ValueError("Negative integers not supported") 43 | return 1 if value == 0 else 1 << ceil(log2(value)) 44 | -------------------------------------------------------------------------------- /scalecodec/updater.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | import os 17 | import requests 18 | 19 | from scalecodec.type_registry import SUPPORTED_TYPE_REGISTRY_PRESETS, ONLINE_BASE_URL 20 | 21 | 22 | def update_type_registries(): 23 | 24 | for type_registry in SUPPORTED_TYPE_REGISTRY_PRESETS: 25 | 26 | result = requests.get(f'{ONLINE_BASE_URL}{type_registry}.json') 27 | 28 | if result.status_code == 200: 29 | remote_type_reg = result.content 30 | 31 | module_path = os.path.dirname(__file__) 32 | path = os.path.join(module_path, 'type_registry/{}.json'.format(type_registry)) 33 | 34 | f = open(path, 'wb') 35 | f.write(remote_type_reg) 36 | f.close() 37 | 38 | 39 | if __name__ == '__main__': 40 | print('Updating type registries...') 41 | update_type_registries() 42 | print('Type registries updated') 43 | -------------------------------------------------------------------------------- /scalecodec/type_registry/rococo.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 9103, 3 | "types": { 4 | "Address": "MultiAddress", 5 | "LookupSource": "MultiAddress", 6 | "AccountInfo": "AccountInfoWithTripleRefCount", 7 | "FullIdentification": "()", 8 | "Keys": "SessionKeys7B", 9 | "CompactAssignments": "CompactAssignmentsFrom265", 10 | "RawSolution": "RawSolutionWith24", 11 | "AssetInstance": "AssetInstanceV0", 12 | "MultiAsset": "MultiAssetV0", 13 | "MultiLocation": "MultiLocationV0", 14 | "Response": "ResponseV0", 15 | "Xcm": "XcmV0", 16 | "XcmOrder": "XcmOrderV0" 17 | }, 18 | "versioning": [ 19 | { 20 | "runtime_range": [0, 200], 21 | "types": { 22 | "Address": "AccountIdAddress", 23 | "LookupSource": "AccountIdAddress", 24 | "AccountInfo": "AccountInfoWithDualRefCount" 25 | } 26 | }, 27 | { 28 | "runtime_range": [201, null], 29 | "types": { 30 | "Address": "MultiAddress", 31 | "LookupSource": "MultiAddress", 32 | "AccountInfo": "AccountInfoWithTripleRefCount" 33 | } 34 | }, 35 | { 36 | "runtime_range": [0, 228], 37 | "types": { 38 | "Keys": "SessionKeys6" 39 | } 40 | }, 41 | { 42 | "runtime_range": [229, null], 43 | "types": { 44 | "Keys": "SessionKeys7B" 45 | } 46 | }, 47 | { 48 | "runtime_range": [0, 9009], 49 | "types": { 50 | "CompactAssignments": "CompactAssignmentsFrom258" 51 | } 52 | }, 53 | { 54 | "runtime_range": [9010, null], 55 | "types": { 56 | "CompactAssignments": "CompactAssignmentsFrom265" 57 | } 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | /.idea 106 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Python SCALE-codec Docs 2 | repo_url: https://github.com/polkascan/py-scale-codec 3 | edit_uri: edit/master/docs/ 4 | site_description: SCALE Codec implementation in Python to communicate with Substrate 5 | 6 | theme: 7 | name: "material" 8 | logo: https://avatars.githubusercontent.com/u/43450475 9 | features: 10 | # - announce.dismiss 11 | - content.action.edit 12 | - content.action.view 13 | - content.code.annotate 14 | - content.code.copy 15 | # - content.tabs.link 16 | - content.tooltips 17 | # - header.autohide 18 | - navigation.expand 19 | - navigation.footer 20 | - navigation.indexes 21 | - navigation.instant 22 | - navigation.prune 23 | # - navigation.sections 24 | - navigation.tabs 25 | - navigation.tabs.sticky 26 | - navigation.top 27 | - navigation.tracking 28 | - search.highlight 29 | - search.share 30 | - search.suggest 31 | - toc.follow 32 | # - toc.integrate 33 | 34 | plugins: 35 | - mkdocstrings: 36 | handlers: 37 | python: 38 | options: 39 | # docstring_section_style: list 40 | members_order: source 41 | show_root_heading: false 42 | show_source: false 43 | show_signature_annotations: true 44 | docstring_style: numpy 45 | heading_level: 2 46 | 47 | - autorefs 48 | - search 49 | 50 | extra: 51 | social: 52 | - icon: fontawesome/brands/github 53 | link: https://github.com/polkascan 54 | - icon: fontawesome/brands/twitter 55 | link: https://twitter.com/polkascan 56 | 57 | nav: 58 | - Overview: index.md 59 | - Examples: examples.md 60 | - Function Reference: 61 | - Base: reference/base.md 62 | - Types: reference/types.md 63 | - Type registry: reference/type_registry.md 64 | - Utils: 65 | - reference/utils/ss58.md 66 | - reference/utils/math.md 67 | 68 | markdown_extensions: 69 | - toc: 70 | permalink: true 71 | toc_depth: 4 72 | - pymdownx.highlight: 73 | linenums: true 74 | - pymdownx.inlinehilite 75 | - pymdownx.snippets 76 | - pymdownx.superfences 77 | 78 | -------------------------------------------------------------------------------- /scalecodec/type_registry/moonriver.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 1001, 3 | "types": { 4 | "EthereumAddress": "H160", 5 | "Address": "EthereumAddress", 6 | "EthAddress": "EthereumAddress", 7 | "LookupSource": "EthereumAddress", 8 | "AccountId": "GenericEthereumAccountId", 9 | "AuthorId": "GenericAccountId", 10 | "ExtrinsicSignature": "EcdsaSignature", 11 | "BlockV0": { 12 | "type": "struct", 13 | "type_mapping": [ 14 | ["header", "EthHeader"], 15 | ["transactions", "Vec"], 16 | ["ommers", "Vec"] 17 | ] 18 | }, 19 | "EthTransactionAction": { 20 | "type": "enum", 21 | "type_mapping": [ 22 | [ 23 | "Call", 24 | "H160" 25 | ], 26 | [ 27 | "Create", 28 | "Null" 29 | ] 30 | ] 31 | }, 32 | "EthTransactionSignature": { 33 | "type": "struct", 34 | "type_mapping": [ 35 | ["v", "u64"], 36 | ["r", "H256"], 37 | ["s", "H256"] 38 | ] 39 | }, 40 | "LegacyTransaction": { 41 | "type": "struct", 42 | "type_mapping": [ 43 | ["nonce", "U256"], 44 | ["gas_price", "U256"], 45 | ["gas_limit", "U256"], 46 | ["action", "EthTransactionAction"], 47 | ["value", "U256"], 48 | ["input", "Bytes"], 49 | ["signature", "EthTransactionSignature"] 50 | ] 51 | }, 52 | "Transaction": "LegacyTransaction", 53 | "Log": "EvmLog", 54 | "EthBloom": "H2048", 55 | "Receipt": { 56 | "type": "struct", 57 | "type_mapping": [ 58 | ["transactionHash", "Option"], 59 | ["transactionIndex", "Option"], 60 | ["blockHash", "Option"], 61 | ["from", "Option"], 62 | ["to", "Option"], 63 | ["blockNumber", "Option"], 64 | ["cumulativeGasUsed", "U256"], 65 | ["gasUsed", "Option"], 66 | ["contractAddress", "Option"], 67 | ["logs", "Vec"], 68 | ["root", "Option"], 69 | ["logsBloom", "EthBloom"], 70 | ["statusCode", "Option"] 71 | ] 72 | }, 73 | "EthStorageProof": { 74 | "type": "struct", 75 | "type_mapping": [ 76 | ["key", "U256"], 77 | ["value", "U256"], 78 | ["proof", "Vec"] 79 | ] 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /scalecodec/type_registry/__init__.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import os 18 | import json 19 | from typing import Optional 20 | 21 | import requests 22 | 23 | SUPPORTED_TYPE_REGISTRY_PRESETS = ('canvas', 'legacy', 'kusama', 'polkadot', 'rococo', 'core', 24 | 'substrate-node-template', 'westend', 'statemint', 'statemine', 'karura', 25 | 'moonbeam', 'moonriver', 'moonbase-alpha', 'crust', 'polymesh-mainnet', 26 | 'polymesh-testnet', 'acala', 'test', 'contracts-on-rococo') 27 | 28 | ONLINE_BASE_URL = 'https://raw.githubusercontent.com/polkascan/py-scale-codec/v1.0/scalecodec/type_registry/' 29 | 30 | 31 | def load_type_registry_preset(name: str, use_remote_preset: bool = False) -> Optional[dict]: 32 | """ 33 | Loads a type registry JSON file into a dict 34 | 35 | Parameters 36 | ---------- 37 | name 38 | use_remote_preset: When True preset is downloaded from Github master, otherwise use files from local installed scalecodec package 39 | 40 | Returns 41 | ------- 42 | 43 | """ 44 | 45 | if name not in SUPPORTED_TYPE_REGISTRY_PRESETS: 46 | raise ValueError(f'Unsupported type registry preset "{name}"') 47 | 48 | if use_remote_preset is True: 49 | result = requests.get(f'{ONLINE_BASE_URL}{name}.json') 50 | 51 | if result.status_code == 200: 52 | return result.json() 53 | else: 54 | module_path = os.path.dirname(__file__) 55 | path = os.path.join(module_path, '{}.json'.format(name)) 56 | try: 57 | return load_type_registry_file(path) 58 | except FileNotFoundError: 59 | return None 60 | 61 | 62 | def load_type_registry_file(file_path: str) -> dict: 63 | 64 | with open(os.path.abspath(file_path), 'r') as fp: 65 | data = fp.read() 66 | 67 | return json.loads(data) 68 | -------------------------------------------------------------------------------- /scalecodec/type_registry/canvas.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 10, 3 | "types": { 4 | "Keys": { 5 | "type": "struct", 6 | "type_mapping": [ 7 | ["aura", "AccountId"], 8 | ["grandpa", "AccountId"] 9 | ] 10 | }, 11 | "ContractExecResult": "ContractExecResultTo267" 12 | }, 13 | "versioning": [ 14 | { 15 | "runtime_range": [0, 9], 16 | "types": { 17 | "AliveContractInfo": { 18 | "type": "struct", 19 | "type_mapping": [ 20 | [ 21 | "trie_id", 22 | "TrieId" 23 | ], 24 | [ 25 | "storage_size", 26 | "u32" 27 | ], 28 | [ 29 | "empty_pair_count", 30 | "u32" 31 | ], 32 | [ 33 | "total_pair_count", 34 | "u32" 35 | ], 36 | [ 37 | "code_hash", 38 | "CodeHash" 39 | ], 40 | [ 41 | "rent_allowance", 42 | "Balance" 43 | ], 44 | [ 45 | "deduct_block", 46 | "BlockNumber" 47 | ], 48 | [ 49 | "last_write", 50 | "Option" 51 | ] 52 | ] 53 | } 54 | } 55 | }, 56 | { 57 | "runtime_range": [10, null], 58 | "types": { 59 | "AliveContractInfo": { 60 | "type": "struct", 61 | "type_mapping": [ 62 | [ 63 | "trie_id", 64 | "TrieId" 65 | ], 66 | [ 67 | "storage_size", 68 | "u32" 69 | ], 70 | [ 71 | "pair_count", 72 | "u32" 73 | ], 74 | [ 75 | "code_hash", 76 | "CodeHash" 77 | ], 78 | [ 79 | "rent_allowance", 80 | "Balance" 81 | ], 82 | [ 83 | "rent_paid", 84 | "Balance" 85 | ], 86 | [ 87 | "deduct_block", 88 | "BlockNumber" 89 | ], 90 | [ 91 | "last_write", 92 | "Option" 93 | ], 94 | [ 95 | "_reserved", 96 | "Option" 97 | ] 98 | ] 99 | } 100 | } 101 | } 102 | ] 103 | } 104 | -------------------------------------------------------------------------------- /test/test_scalebytes.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # test_scalebytes.py 18 | # 19 | 20 | import unittest 21 | 22 | from scalecodec.base import ScaleDecoder, ScaleBytes, RuntimeConfiguration 23 | from scalecodec.exceptions import RemainingScaleBytesNotEmptyException 24 | 25 | 26 | class TestScaleBytes(unittest.TestCase): 27 | 28 | def test_unknown_data_format(self): 29 | self.assertRaises(ValueError, ScaleBytes, 123) 30 | self.assertRaises(ValueError, ScaleBytes, 'test') 31 | 32 | def test_bytes_data_format(self): 33 | obj = RuntimeConfiguration().create_scale_object('Compact', ScaleBytes(b"\x02\x09\x3d\x00")) 34 | obj.decode() 35 | self.assertEqual(obj.value, 1000000) 36 | 37 | def test_remaining_bytes(self): 38 | scale = ScaleBytes("0x01020304") 39 | scale.get_next_bytes(1) 40 | self.assertEqual(scale.get_remaining_bytes(), b'\x02\x03\x04') 41 | 42 | def test_reset(self): 43 | scale = ScaleBytes("0x01020304") 44 | scale.get_next_bytes(1) 45 | scale.reset() 46 | self.assertEqual(scale.get_remaining_bytes(), b'\x01\x02\x03\x04') 47 | 48 | def test_abstract_process(self): 49 | self.assertRaises(NotImplementedError, ScaleDecoder.process, None) 50 | 51 | def test_abstract_encode(self): 52 | self.assertRaises(NotImplementedError, ScaleDecoder.process_encode, None, None) 53 | 54 | def test_add_scalebytes(self): 55 | scale_total = ScaleBytes("0x0102") + "0x0304" 56 | 57 | self.assertEqual(scale_total.data, bytearray.fromhex("01020304")) 58 | 59 | def test_scale_bytes_compare(self): 60 | self.assertEqual(ScaleBytes('0x1234'), ScaleBytes('0x1234')) 61 | self.assertNotEqual(ScaleBytes('0x1234'), ScaleBytes('0x555555')) 62 | 63 | def test_scale_decoder_remaining_bytes(self): 64 | obj = RuntimeConfiguration().create_scale_object('[u8; 3]', ScaleBytes("0x010203")) 65 | self.assertEqual(obj.get_remaining_bytes(), b"\x01\x02\x03") 66 | 67 | def test_no_more_bytes_available(self): 68 | obj = RuntimeConfiguration().create_scale_object('[u8; 4]', ScaleBytes("0x010203")) 69 | with self.assertRaises(RemainingScaleBytesNotEmptyException): 70 | obj.decode(check_remaining=False) 71 | 72 | def test_str_representation(self): 73 | obj = RuntimeConfiguration().create_scale_object('String', ScaleBytes("0x1054657374")) 74 | obj.decode() 75 | self.assertEqual(str(obj), "Test") 76 | 77 | def test_type_convert(self): 78 | self.assertEqual(ScaleDecoder.convert_type("::Type"), "Compact") 79 | self.assertEqual(ScaleDecoder.convert_type("::Type"), "Compact") 80 | self.assertEqual(ScaleDecoder.convert_type("::Type"), "Compact") 81 | 82 | -------------------------------------------------------------------------------- /test/test_runtime_configuration.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # test_runtime_configuration.py 18 | # 19 | 20 | import unittest 21 | 22 | from scalecodec import Struct 23 | from scalecodec.base import RuntimeConfiguration, RuntimeConfigurationObject 24 | from scalecodec.type_registry import load_type_registry_preset 25 | 26 | 27 | class TestScaleDecoderClasses(unittest.TestCase): 28 | 29 | @classmethod 30 | def setUpClass(cls): 31 | cls.runtime_config = RuntimeConfigurationObject() 32 | cls.runtime_config.clear_type_registry() 33 | cls.runtime_config.update_type_registry(load_type_registry_preset("core")) 34 | cls.runtime_config.update_type_registry(load_type_registry_preset("legacy")) 35 | 36 | def test_valid_decoding_classes(self): 37 | for type_string in self.runtime_config.type_registry['types'].keys(): 38 | 39 | decoding_cls = self.runtime_config.get_decoder_class(type_string) 40 | 41 | self.assertIsNotNone(decoding_cls, msg='"{}" didn\'t return decoding class'.format(type_string)) 42 | 43 | 44 | class TestMultipleRuntimeConfigurations(unittest.TestCase): 45 | 46 | def test_use_config_singleton(self): 47 | RuntimeConfiguration(config_id='test').update_type_registry({ 48 | 'types': { 49 | 'CustomTestType': 'u8' 50 | } 51 | }) 52 | self.assertIsNone(RuntimeConfiguration().get_decoder_class('CustomTestType')) 53 | self.assertIsNotNone(RuntimeConfiguration(config_id='test').get_decoder_class('CustomTestType')) 54 | 55 | def test_multiple_instances(self): 56 | runtime_config1 = RuntimeConfigurationObject() 57 | runtime_config1.update_type_registry({ 58 | 'types': { 59 | 'MyNewType': 'Vec' 60 | } 61 | }) 62 | 63 | runtime_config2 = RuntimeConfigurationObject() 64 | 65 | self.assertIsNone(RuntimeConfigurationObject().get_decoder_class('MyNewType')) 66 | self.assertIsNotNone(runtime_config1.get_decoder_class('MyNewType')) 67 | self.assertIsNone(runtime_config2.get_decoder_class('MyNewType')) 68 | 69 | 70 | class TestRuntimeIdCache(unittest.TestCase): 71 | 72 | def test_runtime_id_cache_lookup(self): 73 | runtime_config = RuntimeConfigurationObject() 74 | runtime_config.update_type_registry(load_type_registry_preset("legacy")) 75 | runtime_config.update_type_registry(load_type_registry_preset("kusama")) 76 | 77 | self.assertEqual(1023, runtime_config.get_runtime_id_from_upgrades(54248)) 78 | self.assertEqual(1020, runtime_config.get_runtime_id_from_upgrades(0)) 79 | 80 | def test_set_head(self): 81 | runtime_config = RuntimeConfigurationObject() 82 | runtime_config.update_type_registry(load_type_registry_preset("legacy")) 83 | runtime_config.update_type_registry(load_type_registry_preset("kusama")) 84 | 85 | self.assertIsNone(runtime_config.get_runtime_id_from_upgrades(99999999998)) 86 | 87 | # Set head to block 88 | runtime_config.set_runtime_upgrades_head(99999999999) 89 | 90 | # Check updated cache 91 | self.assertGreater(runtime_config.get_runtime_id_from_upgrades(99999999998), 0) 92 | 93 | 94 | if __name__ == '__main__': 95 | unittest.main() 96 | -------------------------------------------------------------------------------- /scalecodec/type_registry/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": { 3 | "BlockNumber": "U64", 4 | "SpecificTestType": "u32", 5 | "DigestItem": { 6 | "type": "enum", 7 | "type_mapping": [ 8 | ["Other", "Vec"], 9 | ["AuthoritiesChange", "Vec"], 10 | ["ChangesTrieRoot", "Hash"], 11 | ["SealV0", "SealV0"], 12 | ["Consensus", "Consensus"], 13 | ["Seal", "Seal"], 14 | ["PreRuntime", "PreRuntime"] 15 | ] 16 | }, 17 | "IdentityFields": { 18 | "type": "set", 19 | "value_list": { 20 | "Display": 1, 21 | "Legal": 2, 22 | "Web": 4, 23 | "Riot": 8, 24 | "Email": 16, 25 | "PgpFingerprint": 32, 26 | "Image": 64, 27 | "Twitter": 128 28 | } 29 | }, 30 | "EnumWithBaseClass": { 31 | "type": "enum", 32 | "base_class": "GenericContractExecResult", 33 | "type_mapping": [ 34 | [ 35 | "Success", 36 | "ContractExecResultSuccessTo260" 37 | ], 38 | [ 39 | "Error", 40 | "Null" 41 | ] 42 | ] 43 | }, 44 | "EnumWithoutBaseClass": { 45 | "type": "enum", 46 | "type_mapping": [ 47 | [ 48 | "Success", 49 | "ContractExecResultSuccessTo260" 50 | ], 51 | [ 52 | "Error", 53 | "Null" 54 | ] 55 | ] 56 | }, 57 | "StructWithBaseClass": { 58 | "type": "struct", 59 | "base_class": "GenericContractExecResult", 60 | "type_mapping": [ 61 | [ 62 | "Success", 63 | "ContractExecResultSuccessTo260" 64 | ], 65 | [ 66 | "Error", 67 | "Null" 68 | ] 69 | ] 70 | }, 71 | "StructWithoutBaseClass": { 72 | "type": "struct", 73 | "type_mapping": [ 74 | [ 75 | "Success", 76 | "ContractExecResultSuccessTo260" 77 | ], 78 | [ 79 | "Error", 80 | "Null" 81 | ] 82 | ] 83 | }, 84 | "StructWithNestedStruct": { 85 | "type": "struct", 86 | "type_mapping": [ 87 | [ 88 | "flat", 89 | "u8" 90 | ], 91 | [ 92 | "nested", 93 | { 94 | "a": "u8", 95 | "b": "u8" 96 | } 97 | ], 98 | [ 99 | "after", 100 | "u8" 101 | ] 102 | ] 103 | }, 104 | "SetWithoutBaseClass": { 105 | "type": "set", 106 | "value_type": "u32", 107 | "value_list": { 108 | "Value1": 1, 109 | "Value2": 2, 110 | "Value3": 4, 111 | "Value4": 8, 112 | "Value5": 16 113 | } 114 | }, 115 | "SetWithBaseClass": { 116 | "type": "set", 117 | "base_class": "GenericContractExecResult", 118 | "value_type": "u32", 119 | "value_list": { 120 | "Value1": 1, 121 | "Value2": 2, 122 | "Value3": 4, 123 | "Value4": 8, 124 | "Value5": 16 125 | } 126 | }, 127 | "EnumSpecifiedIndex": { 128 | "type": "enum", 129 | "value_list": { 130 | "ACA": 0, 131 | "AUSD": 1, 132 | "DOT": 2, 133 | "LDOT": 3, 134 | "RENBTC": 4, 135 | "KAR": 128, 136 | "KUSD": 129, 137 | "KSM": 130, 138 | "LKSM": 131, 139 | "CASH": 140 140 | } 141 | }, 142 | "EnumWithNestedStruct": { 143 | "type": "enum", 144 | "type_mapping": [ 145 | [ 146 | "Flat", 147 | "u8" 148 | ], 149 | [ 150 | "Nested", 151 | { 152 | "a": "u8", 153 | "b": "u8" 154 | } 155 | ], 156 | [ 157 | "After", 158 | "u8" 159 | ] 160 | ] 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /scalecodec/type_registry/westend.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 9070, 3 | "types": { 4 | "Address": "MultiAddress", 5 | "LookupSource": "MultiAddress", 6 | "AccountInfo": "AccountInfoWithTripleRefCount", 7 | "BlockNumber": "U32", 8 | "LeasePeriod": "BlockNumber", 9 | "Keys": "SessionKeys6", 10 | "ValidatorPrefs": "ValidatorPrefsWithBlocked", 11 | "ProxyType": { 12 | "type": "enum", 13 | "value_list": [ 14 | "Any", 15 | "NonTransfer", 16 | "Staking", 17 | "SudoBalances", 18 | "IdentityJudgement", 19 | "CancelProxy" 20 | ] 21 | }, 22 | "RefCount": "u32", 23 | "CompactAssignments": "CompactAssignmentsFrom258" 24 | }, 25 | "versioning": [ 26 | { 27 | "runtime_range": [3, 22], 28 | "types": { 29 | "OpenTip": { 30 | "type": "struct", 31 | "type_mapping": [ 32 | ["reason", "Hash"], 33 | ["who", "AccountId"], 34 | ["finder", "Option"], 35 | ["closes", "Option"], 36 | ["tips", "Vec"] 37 | ] 38 | } 39 | } 40 | }, 41 | { 42 | "runtime_range": [23, null], 43 | "types": { 44 | "OpenTip": { 45 | "type": "struct", 46 | "type_mapping": [ 47 | ["reason", "Hash"], 48 | ["who", "AccountId"], 49 | ["finder", "AccountId"], 50 | ["deposit", "Balance"], 51 | ["closes", "Option"], 52 | ["tips", "Vec"], 53 | ["finders_fee", "bool"] 54 | ] 55 | } 56 | } 57 | }, 58 | { 59 | "runtime_range": [3, 44], 60 | "types": { 61 | "RefCount": "u8" 62 | } 63 | }, 64 | { 65 | "runtime_range": [45, null], 66 | "types": { 67 | "RefCount": "u32" 68 | } 69 | }, 70 | { 71 | "runtime_range": [1, 42], 72 | "types": { 73 | "CompactAssignments": "CompactAssignmentsTo257" 74 | } 75 | }, 76 | { 77 | "runtime_range": [43, null], 78 | "types": { 79 | "CompactAssignments": "CompactAssignmentsFrom258" 80 | } 81 | }, 82 | { 83 | "runtime_range": [0, 47], 84 | "types": { 85 | "Address": "AccountIdAddress", 86 | "LookupSource": "AccountIdAddress", 87 | "AccountInfo": "AccountInfoWithRefCount", 88 | "Keys": { 89 | "type": "struct", 90 | "type_mapping": [ 91 | ["grandpa", "AccountId"], 92 | ["babe", "AccountId"], 93 | ["im_online", "AccountId"], 94 | ["authority_discovery", "AccountId"], 95 | ["parachains", "AccountId"] 96 | ] 97 | }, 98 | "ValidatorPrefs": "ValidatorPrefsWithCommission" 99 | } 100 | }, 101 | { 102 | "runtime_range": [48, null], 103 | "types": { 104 | "Address": "MultiAddress", 105 | "LookupSource": "MultiAddress", 106 | "ValidatorPrefs": "ValidatorPrefsWithBlocked" 107 | } 108 | }, 109 | { 110 | "runtime_range": [48, 49], 111 | "types": { 112 | "AccountInfo": "AccountInfoWithDualRefCount" 113 | } 114 | }, 115 | { 116 | "runtime_range": [50, null], 117 | "types": { 118 | "AccountInfo": "AccountInfoWithTripleRefCount" 119 | } 120 | }, 121 | { 122 | "runtime_range": [48, null], 123 | "types": { 124 | "Keys": "SessionKeys6" 125 | } 126 | } 127 | ], 128 | "runtime_upgrades": [ 129 | [214356, 4], [392764, 7], [409740, 8], [809976, 20], [877581, 24], 130 | [879238, 25], [889472, 26], [902937, 27], [932751, 28], [991142, 29], 131 | [1030162, 31], [1119657, 32], [1199282, 33], [1342534, 34], [1392263, 35], 132 | [1431703, 36], [1433369, 37], [1490972, 41], [2087397, 43], [2316688, 44], 133 | [2549864, 45], [3925782, 46], [3925843, 47], [4207800, 48], [4627944, 49], 134 | [5124076, 50], [5478664, 900], [5482450, 9000], [5584305, 9010], [5784566, 9030], 135 | [5879822, 9031], [5896856, 9032], [5897316, 9033], [6117927, 9050], [6210274, 9070] 136 | ] 137 | } 138 | -------------------------------------------------------------------------------- /scalecodec/type_registry/polkadot.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 9122, 3 | "types": { 4 | "Address": "MultiAddress", 5 | "LookupSource": "MultiAddress", 6 | "AccountInfo": "AccountInfoWithTripleRefCount", 7 | "BlockNumber": "U32", 8 | "LeasePeriod": "BlockNumber", 9 | "Weight": "WeightV1", 10 | "Keys": { 11 | "type": "struct", 12 | "type_mapping": [ 13 | ["grandpa", "AccountId"], 14 | ["babe", "AccountId"], 15 | ["im_online", "AccountId"], 16 | ["para_validator", "AccountId"], 17 | ["para_assignment", "AccountId"], 18 | ["authority_discovery", "AccountId"] 19 | ] 20 | }, 21 | "ValidatorPrefs": "ValidatorPrefsWithBlocked", 22 | "DispatchInfo": { 23 | "type": "struct", 24 | "type_mapping": [ 25 | ["weight", "Weight"], 26 | ["class", "DispatchClass"], 27 | ["pays_fee", "Pays"] 28 | ] 29 | }, 30 | "ProxyType": { 31 | "type": "enum", 32 | "value_list": [ 33 | "Any", 34 | "NonTransfer", 35 | "Governance", 36 | "Staking", 37 | "DeprecatedSudoBalances", 38 | "IdentityJudgement", 39 | "CancelProxy" 40 | ] 41 | }, 42 | "RefCount": "u32", 43 | "CompactAssignments": "CompactAssignmentsWith16" 44 | }, 45 | "versioning": [ 46 | { 47 | "runtime_range": [0, 12], 48 | "types": { 49 | "OpenTip": { 50 | "type": "struct", 51 | "type_mapping": [ 52 | ["reason", "Hash"], 53 | ["who", "AccountId"], 54 | ["finder", "Option"], 55 | ["closes", "Option"], 56 | ["tips", "Vec"] 57 | ] 58 | } 59 | } 60 | }, 61 | { 62 | "runtime_range": [13, null], 63 | "types": { 64 | "OpenTip": { 65 | "type": "struct", 66 | "type_mapping": [ 67 | ["reason", "Hash"], 68 | ["who", "AccountId"], 69 | ["finder", "AccountId"], 70 | ["deposit", "Balance"], 71 | ["closes", "Option"], 72 | ["tips", "Vec"], 73 | ["finders_fee", "bool"] 74 | ] 75 | } 76 | } 77 | }, 78 | { 79 | "runtime_range": [0, 22], 80 | "types": { 81 | "CompactAssignments": "CompactAssignmentsTo257" 82 | } 83 | }, 84 | { 85 | "runtime_range": [23, null], 86 | "types": { 87 | "CompactAssignments": "CompactAssignmentsWith16" 88 | } 89 | }, 90 | { 91 | "runtime_range": [0, 24], 92 | "types": { 93 | "RefCount": "u8" 94 | } 95 | }, 96 | { 97 | "runtime_range": [25, null], 98 | "types": { 99 | "RefCount": "u32" 100 | } 101 | }, 102 | { 103 | "runtime_range": [0, 27], 104 | "types": { 105 | "Address": "AccountIdAddress", 106 | "LookupSource": "AccountIdAddress", 107 | "AccountInfo": "AccountInfoWithRefCount", 108 | "Keys": { 109 | "type": "struct", 110 | "type_mapping": [ 111 | ["grandpa", "AccountId"], 112 | ["babe", "AccountId"], 113 | ["im_online", "AccountId"], 114 | ["authority_discovery", "AccountId"], 115 | ["parachains", "AccountId"] 116 | ] 117 | }, 118 | "ValidatorPrefs": "ValidatorPrefsWithCommission" 119 | } 120 | }, 121 | { 122 | "runtime_range": [28, null], 123 | "types": { 124 | "Address": "MultiAddress", 125 | "LookupSource": "MultiAddress", 126 | "AccountInfo": "AccountInfoWithDualRefCount", 127 | "Keys": { 128 | "type": "struct", 129 | "type_mapping": [ 130 | ["grandpa", "AccountId"], 131 | ["babe", "AccountId"], 132 | ["im_online", "AccountId"], 133 | ["para_validator", "AccountId"], 134 | ["para_assignment", "AccountId"], 135 | ["authority_discovery", "AccountId"] 136 | ] 137 | }, 138 | "ValidatorPrefs": "ValidatorPrefsWithBlocked" 139 | } 140 | }, 141 | { 142 | "runtime_range": [28, 29], 143 | "types": { 144 | "AccountInfo": "AccountInfoWithDualRefCount" 145 | } 146 | }, 147 | { 148 | "runtime_range": [30, null], 149 | "types": { 150 | "AccountInfo": "AccountInfoWithTripleRefCount" 151 | } 152 | } 153 | ], 154 | "runtime_upgrades": [ 155 | [0, 0], [29231, 1], [188836, 5], [199405, 6], [214264, 7], 156 | [244358, 8], [303079, 9], [314201, 10], [342400, 11], [443963, 12], 157 | [528470, 13], [687751, 14], [746085, 15], [787923, 16], [799302, 17], 158 | [1205128, 18], [1603423, 23], [1733218, 24], [2005673, 25], [2436698, 26], 159 | [3613564, 27], [3899547, 28], [4345767, 29], [4876134, 30], [5661442, 9050], 160 | [6321619, 9080], [6713249, 9090] 161 | ] 162 | } 163 | -------------------------------------------------------------------------------- /docs/examples.md: -------------------------------------------------------------------------------- 1 | ## Code Examples 2 | 3 | ### Encode a Call 4 | 5 | ```python 6 | runtime_config = RuntimeConfigurationObject() 7 | 8 | # This types are all hardcoded types needed to decode metadata types 9 | runtime_config.update_type_registry(load_type_registry_preset(name="core")) 10 | 11 | # Decode retrieved metadata from the RPC 12 | metadata = runtime_config.create_scale_object( 13 | 'MetadataVersioned', data=ScaleBytes(response.get('result')) 14 | ) 15 | metadata.decode() 16 | 17 | # Add the embedded type registry to the runtime config 18 | runtime_config.add_portable_registry(metadata) 19 | 20 | call = runtime_config.create_scale_object( 21 | "Call", metadata=metadata 22 | ) 23 | call.encode({ 24 | "call_module": "Balances", 25 | "call_function": "transfer", 26 | "call_args": {"dest": "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", "value": 3}, 27 | }) 28 | ``` 29 | 30 | ### Decode the result of a `state_getStorageAt` RPC call 31 | 32 | ```python 33 | event_data = "0x2000000000000000b0338609000000000200000001000000000080b2e60e0000000002000000020000000003be1957935299d0be2f35b8856751feab95fc7089239366b52b72ca98249b94300000020000000500be1957935299d0be2f35b8856751feab95fc7089239366b52b72ca98249b943000264d2823000000000000000000000000000200000005027a9650a6bd43f1e0b4546affb88f8c14213e1fb60512692c2b39fbfcfc56b703be1957935299d0be2f35b8856751feab95fc7089239366b52b72ca98249b943000264d2823000000000000000000000000000200000013060c4c700700000000000000000000000000000200000005047b8441d5110c178c29be709793a41d73ae8b3119a971b18fbd20945ea5d622f00313dc01000000000000000000000000000002000000000010016b0b00000000000000" 34 | 35 | system_pallet = metadata.get_metadata_pallet("System") 36 | event_storage_function = system_pallet.get_storage_function("Events") 37 | 38 | event = runtime_config.create_scale_object( 39 | event_storage_function.get_value_type_string(), metadata=metadata 40 | ) 41 | print(event.decode(ScaleBytes(event_data))) 42 | ``` 43 | 44 | ### Generate type decomposition information 45 | 46 | The function `generate_type_decomposition` can be 47 | used when more information is needed how to encode a certain SCALE type: 48 | 49 | _Example 1_ 50 | ```python 51 | scale_obj = runtime_config.create_scale_object("RawBabePreDigest") 52 | 53 | type_info = scale_obj.generate_type_decomposition() 54 | 55 | print(type_info) 56 | # { 57 | # 'Phantom': None, 58 | # 'Primary': {'authority_index': 'u32', 'slot_number': 'u64', 'vrf_output': '[u8; 32]', 'vrf_proof': '[u8; 64]'}, 59 | # 'SecondaryPlain': {'authority_index': 'u32', 'slot_number': 'u64'}, 60 | # 'SecondaryVRF': {'authority_index': 'u32', 'slot_number': 'u64', 'vrf_output': '[u8; 32]', 'vrf_proof': '[u8; 64]'} 61 | # } 62 | ``` 63 | 64 | _Example 2_ 65 | ```python 66 | pallet = metadata.get_metadata_pallet("Tokens") 67 | storage_function = pallet.get_storage_function("TotalIssuance") 68 | 69 | param_type_string = storage_function.get_params_type_string() 70 | param_type_obj = runtime_config.create_scale_object(param_type_string[0]) 71 | 72 | type_info = param_type_obj.generate_type_decomposition() 73 | 74 | print(type_info) 75 | # [{ 76 | # 'Token': ('ACA', 'AUSD', 'DOT', 'LDOT', 'RENBTC', 'CASH', 'KAR', 'KUSD', 'KSM', 'LKSM', 'TAI', 'BNC', 'VSKSM', 'PHA', 'KINT', 'KBTC'), 77 | # 'DexShare': ({'Token': ('ACA', 'AUSD', 'DOT', 'LDOT', 'RENBTC', 'CASH', 'KAR', 'KUSD', 'KSM', 'LKSM', 'TAI', 'BNC', 'VSKSM', 'PHA', 'KINT', 'KBTC'), 'Erc20': '[u8; 20]', 'LiquidCrowdloan': 'u32', 'ForeignAsset': 'u16'}, {'Token': ('ACA', 'AUSD', 'DOT', 'LDOT', 'RENBTC', 'CASH', 'KAR', 'KUSD', 'KSM', 'LKSM', 'TAI', 'BNC', 'VSKSM', 'PHA', 'KINT', 'KBTC'), 'Erc20': '[u8; 20]', 'LiquidCrowdloan': 'u32', 'ForeignAsset': 'u16'}), 78 | # 'Erc20': '[u8; 20]', 79 | # 'StableAssetPoolToken': 'u32', 80 | # 'LiquidCrowdloan': 'u32', 81 | # 'ForeignAsset': 'u16' 82 | # }] 83 | ``` 84 | 85 | In the above examples are simple value `Enums` representation as a `tuple` of possible values and complex `Enums` as 86 | a `dict`. 87 | 88 | ## Examples (prior to MetadataV14) 89 | 90 | ### Decode a SCALE-encoded Compact\ 91 | 92 | ```python 93 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("legacy")) 94 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("kusama")) 95 | obj = RuntimeConfiguration().create_scale_object('Compact', data=ScaleBytes("0x130080cd103d71bc22")) 96 | obj.decode() 97 | print(obj.value) 98 | ``` 99 | 100 | ### Encode to Compact\ 101 | 102 | ```python 103 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("legacy")) 104 | obj = RuntimeConfiguration().create_scale_object('Compact') 105 | scale_data = obj.encode(2503000000000000000) 106 | print(scale_data) 107 | ``` 108 | 109 | ### Encode to Vec\ 110 | 111 | ```python 112 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("legacy")) 113 | value = ['test', 'vec'] 114 | obj = RuntimeConfiguration().create_scale_object('Vec') 115 | scale_data = obj.encode(value) 116 | print(scale_data) 117 | ``` 118 | 119 | ### Add custom types to type registry 120 | 121 | ```python 122 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("legacy")) 123 | 124 | custom_types = { 125 | "types": { 126 | "MyCustomType": "u32", 127 | "CustomNextAuthority": { 128 | "type": "struct", 129 | "type_mapping": [ 130 | ["AuthorityId", "AuthorityId"], 131 | ["weight", "AuthorityWeight"] 132 | ] 133 | } 134 | } 135 | } 136 | 137 | RuntimeConfiguration().update_type_registry(custom_types) 138 | ``` 139 | 140 | ### Or from a custom JSON file 141 | 142 | ```python 143 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("legacy")) 144 | RuntimeConfiguration().update_type_registry(load_type_registry_file("/path/to/type_registry.json")) 145 | ``` 146 | 147 | ### Multiple runtime configurations 148 | By default a singleton is used to maintain the configuration, for multiple instances: 149 | 150 | ```python 151 | # Kusama runtime config 152 | runtime_config_kusama = RuntimeConfigurationObject() 153 | runtime_config_kusama.update_type_registry(load_type_registry_preset("legacy")) 154 | runtime_config_kusama.update_type_registry(load_type_registry_preset("kusama")) 155 | 156 | 157 | # Polkadot runtime config 158 | runtime_config_polkadot = RuntimeConfigurationObject() 159 | runtime_config_polkadot.update_type_registry(load_type_registry_preset("legacy")) 160 | runtime_config_polkadot.update_type_registry(load_type_registry_preset("polkadot")) 161 | 162 | # Decode extrinsic using Kusama runtime configuration 163 | extrinsic = runtime_config_kusama.create_scale_object( 164 | type_string='Extrinsic', 165 | metadata=metadata_decoder 166 | ) 167 | extrinsic.decode(ScaleBytes(extrinsic_data)) 168 | 169 | ``` 170 | -------------------------------------------------------------------------------- /test/test_metadata.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | import os 17 | import pickle 18 | import unittest 19 | 20 | from scalecodec.base import ScaleBytes, RuntimeConfigurationObject 21 | from scalecodec.type_registry import load_type_registry_preset, load_type_registry_file 22 | 23 | 24 | class TestMetadataRegistry(unittest.TestCase): 25 | 26 | @classmethod 27 | def setUpClass(cls): 28 | cls.runtime_config = RuntimeConfigurationObject() 29 | cls.runtime_config.update_type_registry(load_type_registry_preset("core")) 30 | 31 | module_path = os.path.dirname(__file__) 32 | cls.metadata_fixture_dict = load_type_registry_file( 33 | os.path.join(module_path, 'fixtures', 'metadata_hex.json') 34 | ) 35 | 36 | def test_metadata_registry_v9(self): 37 | metadata_obj = self.runtime_config.create_scale_object( 38 | "MetadataVersioned", data=ScaleBytes(self.metadata_fixture_dict['V9']) 39 | ) 40 | metadata_obj.decode() 41 | self.assertEqual(metadata_obj.value_object[1].index, 9) 42 | self.assertIsNone(metadata_obj.portable_registry) 43 | self.assertGreater(len(metadata_obj[1][1]['modules']), 0) 44 | self.assertGreater(len(metadata_obj.value[1]['V9']['modules']), 0) 45 | self.assertGreater(len(metadata_obj.call_index.items()), 0) 46 | 47 | self.assertEqual(len(metadata_obj.get_signed_extensions().items()), 0) 48 | 49 | def test_metadata_registry_v10(self): 50 | metadata_obj = self.runtime_config.create_scale_object( 51 | "MetadataVersioned", data=ScaleBytes(self.metadata_fixture_dict['V10']) 52 | ) 53 | metadata_obj.decode() 54 | self.assertEqual(metadata_obj.value_object[1].index, 10) 55 | self.assertIsNone(metadata_obj.portable_registry) 56 | self.assertGreater(len(metadata_obj[1][1]['modules']), 0) 57 | self.assertGreater(len(metadata_obj.value[1]['V10']['modules']), 0) 58 | self.assertGreater(len(metadata_obj.call_index.items()), 0) 59 | 60 | self.assertEqual(len(metadata_obj.get_signed_extensions().items()), 0) 61 | 62 | def test_metadata_registry_v11(self): 63 | metadata_obj = self.runtime_config.create_scale_object( 64 | "MetadataVersioned", data=ScaleBytes(self.metadata_fixture_dict['V11']) 65 | ) 66 | metadata_obj.decode() 67 | self.assertEqual(metadata_obj.value_object[1].index, 11) 68 | self.assertIsNone(metadata_obj.portable_registry) 69 | self.assertGreater(len(metadata_obj[1][1]['modules']), 0) 70 | self.assertGreater(len(metadata_obj.value[1]['V11']['modules']), 0) 71 | self.assertGreater(len(metadata_obj.call_index.items()), 0) 72 | 73 | self.assertGreater(len(metadata_obj.get_signed_extensions().items()), 0) 74 | 75 | def test_metadata_registry_v12(self): 76 | metadata_obj = self.runtime_config.create_scale_object( 77 | "MetadataVersioned", data=ScaleBytes(self.metadata_fixture_dict['V12']) 78 | ) 79 | metadata_obj.decode() 80 | self.assertEqual(metadata_obj.value_object[1].index, 12) 81 | self.assertIsNone(metadata_obj.portable_registry) 82 | self.assertGreater(len(metadata_obj[1][1]['modules']), 0) 83 | self.assertGreater(len(metadata_obj.value[1]['V12']['modules']), 0) 84 | self.assertGreater(len(metadata_obj.call_index.items()), 0) 85 | 86 | self.assertGreater(len(metadata_obj.get_signed_extensions().items()), 0) 87 | 88 | def test_metadata_registry_v13(self): 89 | 90 | metadata_obj = self.runtime_config.create_scale_object( 91 | "MetadataVersioned", data=ScaleBytes(self.metadata_fixture_dict['V13']) 92 | ) 93 | metadata_obj.decode() 94 | self.assertEqual(metadata_obj.value_object[1].index, 13) 95 | self.assertIsNone(metadata_obj.portable_registry) 96 | self.assertGreater(len(metadata_obj[1][1]['modules']), 0) 97 | self.assertGreater(len(metadata_obj.value[1]['V13']['modules']), 0) 98 | self.assertGreater(len(metadata_obj.call_index.items()), 0) 99 | 100 | self.assertGreater(len(metadata_obj.get_signed_extensions().items()), 0) 101 | 102 | def test_metadata_registry_decode_v14(self): 103 | metadata_obj = self.runtime_config.create_scale_object( 104 | "MetadataVersioned", data=ScaleBytes(self.metadata_fixture_dict['V14']) 105 | ) 106 | metadata_obj.decode() 107 | self.assertEqual(metadata_obj.value_object[1].index, 14) 108 | self.assertIsNotNone(metadata_obj.portable_registry) 109 | 110 | self.assertGreater(len(metadata_obj[1][1]['pallets']), 0) 111 | self.assertGreater(len(metadata_obj.value[1]['V14']['pallets']), 0) 112 | 113 | self.assertGreater(len(metadata_obj.get_signed_extensions().items()), 0) 114 | 115 | # def test_pickle_test(self): 116 | # metadata_obj = self.runtime_config.create_scale_object( 117 | # "MetadataVersioned", data=ScaleBytes(self.metadata_fixture_dict['V14']) 118 | # ) 119 | # metadata_obj.decode() 120 | # 121 | # # for name, decoder_class in self.runtime_config.type_registry['types'].items(): 122 | # # import __main__ 123 | # # 124 | # # globals()[decoder_class.__name__] = decoder_class 125 | # 126 | # # assert(type(metadata_obj) is globals()['MetadataVersioned']) 127 | # 128 | # pickle_data = pickle.dumps(metadata_obj) 129 | 130 | 131 | class TestMetadataTypes(unittest.TestCase): 132 | 133 | metadata_version = 'karura_test' 134 | 135 | @classmethod 136 | def setUpClass(cls): 137 | cls.runtime_config = RuntimeConfigurationObject() 138 | cls.runtime_config.update_type_registry(load_type_registry_preset("core")) 139 | 140 | module_path = os.path.dirname(__file__) 141 | cls.metadata_fixture_dict = load_type_registry_file( 142 | os.path.join(module_path, 'fixtures', 'metadata_hex.json') 143 | ) 144 | 145 | cls.metadata_obj = cls.runtime_config.create_scale_object( 146 | "MetadataVersioned", data=ScaleBytes(cls.metadata_fixture_dict[cls.metadata_version]) 147 | ) 148 | cls.metadata_obj.decode() 149 | 150 | cls.runtime_config.add_portable_registry(cls.metadata_obj) 151 | 152 | def test_storage_function_type_decomposition_simple(self): 153 | pallet = self.metadata_obj.get_metadata_pallet("System") 154 | storage_function = pallet.get_storage_function("BlockHash") 155 | 156 | param_type_string = storage_function.get_params_type_string() 157 | param_type_obj = self.runtime_config.create_scale_object(param_type_string[0]) 158 | 159 | type_info = param_type_obj.generate_type_decomposition() 160 | self.assertEqual('u32', type_info) 161 | 162 | def test_storage_function_type_decomposition_complex(self): 163 | pallet = self.metadata_obj.get_metadata_pallet("Tokens") 164 | storage_function = pallet.get_storage_function("TotalIssuance") 165 | 166 | param_type_string = storage_function.get_params_type_string() 167 | param_type_obj = self.runtime_config.create_scale_object(param_type_string[0]) 168 | 169 | type_info = param_type_obj.generate_type_decomposition() 170 | self.assertIn('Token', type_info) 171 | self.assertEqual('ACA', type_info['Token'][0]) 172 | 173 | 174 | -------------------------------------------------------------------------------- /scalecodec/utils/ss58.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2021 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # ss58.py 18 | 19 | """ SS58 is a simple address format designed for Substrate based chains. 20 | Encoding/decoding according to specification on 21 | https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58) 22 | 23 | """ 24 | from typing import Optional, Union 25 | 26 | import base58 27 | from hashlib import blake2b 28 | 29 | from scalecodec.base import ScaleBytes, RuntimeConfiguration 30 | 31 | 32 | def ss58_decode(address: str, valid_ss58_format: Optional[int] = None) -> str: 33 | """ 34 | Decodes given SS58 encoded address to an account ID 35 | Parameters 36 | ---------- 37 | address: e.g. EaG2CRhJWPb7qmdcJvy3LiWdh26Jreu9Dx6R1rXxPmYXoDk 38 | valid_ss58_format 39 | 40 | Returns 41 | ------- 42 | Decoded string AccountId 43 | """ 44 | 45 | # Check if address is already decoded 46 | if address.startswith('0x'): 47 | return address 48 | 49 | if address == '': 50 | raise ValueError("Empty address provided") 51 | 52 | checksum_prefix = b'SS58PRE' 53 | 54 | address_decoded = base58.b58decode(address) 55 | 56 | if address_decoded[0] & 0b0100_0000: 57 | ss58_format_length = 2 58 | ss58_format = ((address_decoded[0] & 0b0011_1111) << 2) | (address_decoded[1] >> 6) | \ 59 | ((address_decoded[1] & 0b0011_1111) << 8) 60 | else: 61 | ss58_format_length = 1 62 | ss58_format = address_decoded[0] 63 | 64 | if ss58_format in [46, 47]: 65 | raise ValueError(f"{ss58_format} is a reserved SS58 format") 66 | 67 | if valid_ss58_format is not None and ss58_format != valid_ss58_format: 68 | raise ValueError("Invalid SS58 format") 69 | 70 | # Determine checksum length according to length of address string 71 | if len(address_decoded) in [3, 4, 6, 10]: 72 | checksum_length = 1 73 | elif len(address_decoded) in [5, 7, 11, 34 + ss58_format_length, 35 + ss58_format_length]: 74 | checksum_length = 2 75 | elif len(address_decoded) in [8, 12]: 76 | checksum_length = 3 77 | elif len(address_decoded) in [9, 13]: 78 | checksum_length = 4 79 | elif len(address_decoded) in [14]: 80 | checksum_length = 5 81 | elif len(address_decoded) in [15]: 82 | checksum_length = 6 83 | elif len(address_decoded) in [16]: 84 | checksum_length = 7 85 | elif len(address_decoded) in [17]: 86 | checksum_length = 8 87 | else: 88 | raise ValueError("Invalid address length") 89 | 90 | checksum = blake2b(checksum_prefix + address_decoded[0:-checksum_length]).digest() 91 | 92 | if checksum[0:checksum_length] != address_decoded[-checksum_length:]: 93 | raise ValueError("Invalid checksum") 94 | 95 | return address_decoded[ss58_format_length:len(address_decoded)-checksum_length].hex() 96 | 97 | 98 | def ss58_encode(address: Union[str, bytes], ss58_format: int = 42) -> str: 99 | """ 100 | Encodes an account ID to an Substrate address according to provided address_type 101 | 102 | Parameters 103 | ---------- 104 | address 105 | ss58_format 106 | 107 | Returns 108 | ------- 109 | str 110 | """ 111 | checksum_prefix = b'SS58PRE' 112 | 113 | if ss58_format < 0 or ss58_format > 16383 or ss58_format in [46, 47]: 114 | raise ValueError("Invalid value for ss58_format") 115 | 116 | if type(address) is bytes or type(address) is bytearray: 117 | address_bytes = address 118 | else: 119 | address_bytes = bytes.fromhex(address.replace('0x', '')) 120 | 121 | if len(address_bytes) in [32, 33]: 122 | # Checksum size is 2 bytes for public key 123 | checksum_length = 2 124 | elif len(address_bytes) in [1, 2, 4, 8]: 125 | # Checksum size is 1 byte for account index 126 | checksum_length = 1 127 | else: 128 | raise ValueError("Invalid length for address") 129 | 130 | if ss58_format < 64: 131 | ss58_format_bytes = bytes([ss58_format]) 132 | else: 133 | ss58_format_bytes = bytes([ 134 | ((ss58_format & 0b0000_0000_1111_1100) >> 2) | 0b0100_0000, 135 | (ss58_format >> 8) | ((ss58_format & 0b0000_0000_0000_0011) << 6) 136 | ]) 137 | 138 | input_bytes = ss58_format_bytes + address_bytes 139 | checksum = blake2b(checksum_prefix + input_bytes).digest() 140 | 141 | return base58.b58encode(input_bytes + checksum[:checksum_length]).decode() 142 | 143 | 144 | def ss58_encode_account_index(account_index: int, ss58_format: int = 42) -> str: 145 | """ 146 | Encodes an AccountIndex to an Substrate address according to provided address_type 147 | 148 | Parameters 149 | ---------- 150 | account_index 151 | ss58_format 152 | 153 | Returns 154 | ------- 155 | str 156 | """ 157 | 158 | if 0 <= account_index <= 2 ** 8 - 1: 159 | account_idx_encoder = RuntimeConfiguration().create_scale_object('u8') 160 | elif 2 ** 8 <= account_index <= 2 ** 16 - 1: 161 | account_idx_encoder = RuntimeConfiguration().create_scale_object('u16') 162 | elif 2 ** 16 <= account_index <= 2 ** 32 - 1: 163 | account_idx_encoder = RuntimeConfiguration().create_scale_object('u32') 164 | elif 2 ** 32 <= account_index <= 2 ** 64 - 1: 165 | account_idx_encoder = RuntimeConfiguration().create_scale_object('u64') 166 | else: 167 | raise ValueError("Value too large for an account index") 168 | 169 | return ss58_encode(account_idx_encoder.encode(account_index).data, ss58_format) 170 | 171 | 172 | def ss58_decode_account_index(address: str, valid_ss58_format: Optional[int] = None) -> int: 173 | """ 174 | Decodes given SS58 encoded address to an AccountIndex 175 | 176 | Parameters 177 | ---------- 178 | address 179 | valid_ss58_format 180 | 181 | Returns 182 | ------- 183 | Decoded int AccountIndex 184 | """ 185 | 186 | account_index_bytes = ss58_decode(address, valid_ss58_format) 187 | 188 | if len(account_index_bytes) == 2: 189 | return RuntimeConfiguration().create_scale_object( 190 | 'u8', data=ScaleBytes('0x{}'.format(account_index_bytes)) 191 | ).decode() 192 | if len(account_index_bytes) == 4: 193 | return RuntimeConfiguration().create_scale_object( 194 | 'u16', data=ScaleBytes('0x{}'.format(account_index_bytes)) 195 | ).decode() 196 | if len(account_index_bytes) == 8: 197 | return RuntimeConfiguration().create_scale_object( 198 | 'u32', data=ScaleBytes('0x{}'.format(account_index_bytes)) 199 | ).decode() 200 | if len(account_index_bytes) == 16: 201 | return RuntimeConfiguration().create_scale_object( 202 | 'u64', data=ScaleBytes('0x{}'.format(account_index_bytes)) 203 | ).decode() 204 | else: 205 | raise ValueError("Invalid account index length") 206 | 207 | 208 | def is_valid_ss58_address(value: str, valid_ss58_format: Optional[int] = None) -> bool: 209 | """ 210 | Checks if given value is a valid SS58 formatted address, optionally check if address is valid for specified 211 | ss58_format 212 | 213 | Parameters 214 | ---------- 215 | value: value to checked 216 | valid_ss58_format: if valid_ss58_format is provided the address must be valid for specified ss58_format (network) as well 217 | 218 | Returns 219 | ------- 220 | bool 221 | """ 222 | 223 | # Return False in case a public key is provided 224 | if value.startswith('0x'): 225 | return False 226 | 227 | try: 228 | ss58_decode(value, valid_ss58_format=valid_ss58_format) 229 | except ValueError: 230 | return False 231 | 232 | return True 233 | 234 | 235 | def get_ss58_format(ss58_address: str) -> int: 236 | """ 237 | Returns the SS58 format for given SS58 address 238 | 239 | Parameters 240 | ---------- 241 | ss58_address 242 | 243 | Returns 244 | ------- 245 | int 246 | """ 247 | address_decoded = base58.b58decode(ss58_address) 248 | 249 | if address_decoded[0] & 0b0100_0000: 250 | ss58_format = ((address_decoded[0] & 0b0011_1111) << 2) | (address_decoded[1] >> 6) | \ 251 | ((address_decoded[1] & 0b0011_1111) << 8) 252 | else: 253 | ss58_format = address_decoded[0] 254 | 255 | if ss58_format in [46, 47]: 256 | raise ValueError(f"{ss58_format} is a reserved SS58 format") 257 | 258 | return ss58_format 259 | -------------------------------------------------------------------------------- /scalecodec/type_registry/crust.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 18, 3 | "types": { 4 | "AccountInfo": "AccountInfoWithProviders", 5 | "Address": "AccountId", 6 | "AddressInfo": "Vec", 7 | "LookupSource": "AccountId", 8 | "EraBenefits": { 9 | "type": "struct", 10 | "type_mapping": [ 11 | ["total_benefits", "Balance"], 12 | ["total_funds", "Balance"], 13 | ["used_benefits", "Balance"], 14 | ["active_era", "EraIndex"] 15 | ] 16 | }, 17 | "FundsType": { 18 | "type": "enum", 19 | "value_list": ["SWORK", "MARKET"] 20 | }, 21 | "FundsUnlockChunk": { 22 | "type": "struct", 23 | "type_mapping": [ 24 | ["value", "Compact"], 25 | ["era", "Compact"] 26 | ] 27 | }, 28 | "MarketBenefit": { 29 | "type": "struct", 30 | "type_mapping": [ 31 | ["total_funds", "Compact"], 32 | ["active_funds", "Compact"], 33 | ["used_fee_reduction_quota", "Compact"], 34 | ["file_reward", "Compact"], 35 | ["refreshed_at", "Compact"], 36 | ["unlocking_funds", "Vec>"] 37 | ] 38 | }, 39 | "SworkBenefit": { 40 | "type": "struct", 41 | "type_mapping": [ 42 | ["total_funds", "Compact"], 43 | ["active_funds", "Compact"], 44 | ["total_fee_reduction_count", "u32"], 45 | ["used_fee_reduction_count", "u32"], 46 | ["refreshed_at", "Compact"], 47 | ["unlocking_funds", "Vec>"] 48 | ] 49 | }, 50 | "BridgeChainId": "u8", 51 | "ChainId": "u8", 52 | "ResourceId": "H256", 53 | "DepositNonce": "u64", 54 | "ProposalStatus": { 55 | "type": "enum", 56 | "value_list": ["Initiated", "Approved", "Rejected"] 57 | }, 58 | "ProposalVotes": { 59 | "type": "struct", 60 | "type_mapping": [ 61 | ["votes_for", "Vec"], 62 | ["votes_against", "Vec"], 63 | ["status", "ProposalStatus"], 64 | ["expiry", "BlockNumber"] 65 | ] 66 | }, 67 | "Erc721Token": { 68 | "type": "struct", 69 | "type_mapping": [ 70 | ["id", "TokenId"], 71 | ["metadata", "Vec"] 72 | ] 73 | }, 74 | "TokenId": "U256", 75 | "ETHAddress": "Vec", 76 | "EthereumTxHash": "H256", 77 | "Lock": { 78 | "type": "struct", 79 | "type_mapping": [ 80 | ["total", "Compact"], 81 | ["last_unlock_at", "BlockNumber"], 82 | ["lock_type", "LockType"] 83 | ] 84 | }, 85 | "LockType": { 86 | "type": "struct", 87 | "type_mapping": [ 88 | ["delay", "BlockNumber"], 89 | ["lock_period", "u32"] 90 | ] 91 | }, 92 | "FileInfoV2": { 93 | "type": "struct", 94 | "type_mapping": [ 95 | ["file_size", "u64"], 96 | ["spower", "u64"], 97 | ["expired_at", "BlockNumber"], 98 | ["calculated_at", "BlockNumber"], 99 | ["amount", "Compact"], 100 | ["prepaid", "Compact"], 101 | ["reported_replica_count", "u32"], 102 | ["remaining_paid_count", "u32"], 103 | ["replicas", "BTreeMap>"] 104 | ] 105 | }, 106 | "Guarantee": { 107 | "type": "struct", 108 | "type_mapping": [ 109 | ["targets", "Vec>"], 110 | ["total", "Compact"], 111 | ["submitted_in", "EraIndex"], 112 | ["suppressed", "bool"] 113 | ] 114 | }, 115 | "ValidatorPrefs": { 116 | "type": "struct", 117 | "type_mapping": [ 118 | ["guarantee_fee", "Compact"] 119 | ] 120 | }, 121 | "Group": { 122 | "type": "struct", 123 | "type_mapping": [ 124 | ["members", "BTreeSet"], 125 | ["allowlist", "BTreeSet"] 126 | ] 127 | }, 128 | "IASSig": "Vec", 129 | "Identity": { 130 | "type": "struct", 131 | "type_mapping": [ 132 | ["anchor", "SworkerAnchor"], 133 | ["punishment_deadline", "u64"], 134 | ["group", "Option"] 135 | ] 136 | }, 137 | "ISVBody": "Vec", 138 | "MerkleRoot": "Vec", 139 | "ReportSlot": "u64", 140 | "PKInfo": { 141 | "type": "struct", 142 | "type_mapping": [ 143 | ["code", "SworkerCode"], 144 | ["anchor", "Option"] 145 | ] 146 | }, 147 | "SworkerAnchor": "Vec", 148 | "SworkerCert": "Vec", 149 | "SworkerCode": "Vec", 150 | "SworkerPubKey": "Vec", 151 | "SworkerSignature": "Vec", 152 | "WorkReport": { 153 | "type": "struct", 154 | "type_mapping": [ 155 | ["report_slot", "u64"], 156 | ["spower", "u64"], 157 | ["free", "u64"], 158 | ["reported_files_size", "u64"], 159 | ["reported_srd_root", "MerkleRoot"], 160 | ["reported_files_root", "MerkleRoot"] 161 | ] 162 | }, 163 | "FeeReductionBenefit": { 164 | "type": "struct", 165 | "type_mapping": [ 166 | ["funds", "Balance"], 167 | ["total_fee_reduction_count", "u32"], 168 | ["used_fee_reduction_quota", "Balance"], 169 | ["used_fee_reduction_count", "u32"], 170 | ["refreshed_at", "EraIndex"] 171 | ] 172 | }, 173 | "CsmBalance": "Balance", 174 | "CsmBalanceOf": "Balance", 175 | "ETHAddress": "Vec", 176 | "EthereumTxHash": "H256", 177 | "CSMLedger": { 178 | "type": "struct", 179 | "type_mapping": [ 180 | ["total", "Compact"], 181 | ["active", "Compact"], 182 | ["unlocking", "Vec>"] 183 | ] 184 | }, 185 | "CSMUnlockChunk": { 186 | "type": "struct", 187 | "type_mapping": [ 188 | ["value", "Compact"], 189 | ["bn", "Compact"] 190 | ] 191 | }, 192 | "FileInfo": { 193 | "type": "struct", 194 | "type_mapping": [ 195 | ["file_size", "u64"], 196 | ["spower", "u64"], 197 | ["expired_at", "BlockNumber"], 198 | ["calculated_at", "BlockNumber"], 199 | ["amount", "Compact"], 200 | ["prepaid", "Compact"], 201 | ["reported_replica_count", "u32"], 202 | ["replicas", "Vec>"] 203 | ] 204 | }, 205 | "MerchantLedger": { 206 | "type": "struct", 207 | "type_mapping": [ 208 | ["reward", "Balance"], 209 | ["collateral", "Balance"] 210 | ] 211 | }, 212 | "Releases": { 213 | "type": "enum", 214 | "value_list": ["V1_0_0", "V2_0_0"] 215 | }, 216 | "Replica": { 217 | "type": "struct", 218 | "type_mapping": [ 219 | ["who", "AccountId"], 220 | ["valid_at", "BlockNumber"], 221 | ["anchor", "SworkerAnchor"], 222 | ["is_reported", "bool"], 223 | ["created_at", "Option"] 224 | ] 225 | }, 226 | "Status": { 227 | "type": "enum", 228 | "value_list": ["Free", "Reserved"] 229 | }, 230 | "UsedInfo": { 231 | "type": "struct", 232 | "type_mapping": [ 233 | ["used_size", "u64"], 234 | ["reported_group_count", "u32"], 235 | ["groups", "BTreeMap"] 236 | ] 237 | }, 238 | "Guarantee": { 239 | "type": "struct", 240 | "type_mapping": [ 241 | ["targets", "Vec>"], 242 | ["total", "Compact"], 243 | ["submitted_in", "EraIndex"], 244 | ["suppressed", "bool"] 245 | ] 246 | }, 247 | "ValidatorPrefs": { 248 | "type": "struct", 249 | "type_mapping": [ 250 | ["guarantee_fee", "Compact"] 251 | ] 252 | }, 253 | "IASSig": "Vec", 254 | "Identity": { 255 | "type": "struct", 256 | "type_mapping": [ 257 | ["anchor", "SworkerAnchor"], 258 | ["punishment_deadline", "u64"], 259 | ["group", "Option"] 260 | ] 261 | }, 262 | "ISVBody": "Vec", 263 | "MerkleRoot": "Vec", 264 | "ReportSlot": "u64", 265 | "PKInfo": { 266 | "type": "struct", 267 | "type_mapping": [ 268 | ["code", "SworkerCode"], 269 | ["anchor", "Option"] 270 | ] 271 | }, 272 | "SworkerAnchor": "Vec", 273 | "SworkerCert": "Vec", 274 | "SworkerCode": "Vec", 275 | "SworkerPubKey": "Vec", 276 | "SworkerSignature": "Vec", 277 | "WorkReport": { 278 | "type": "struct", 279 | "type_mapping": [ 280 | ["report_slot", "u64"], 281 | ["used", "u64"], 282 | ["free", "u64"], 283 | ["reported_files_size", "u64"], 284 | ["reported_srd_root", "MerkleRoot"], 285 | ["reported_files_root", "MerkleRoot"] 286 | ] 287 | } 288 | }, 289 | "versioning": [ 290 | ] 291 | } 292 | -------------------------------------------------------------------------------- /test/test_ss58.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2021 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import unittest 18 | 19 | from scalecodec.utils.ss58 import ss58_decode, ss58_encode, ss58_encode_account_index, ss58_decode_account_index, \ 20 | is_valid_ss58_address 21 | 22 | 23 | class SS58TestCase(unittest.TestCase): 24 | 25 | @classmethod 26 | def setUpClass(cls) -> None: 27 | 28 | cls.alice_keypair = { 29 | 'address': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', 30 | 'public_key': '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d', 31 | 'ss58_format': 42 32 | } 33 | 34 | cls.subkey_pairs = [ 35 | { 36 | 'address': '5EU9mjvZdLRGyDFiBHjxrxvQuaaBpeTZCguhxM3yMX8cpZ2u', 37 | 'public_key': '0x6a5a5957ce778c174c02c151e7c4917ac127b33ad8485f579f830fc15d31bc5a', 38 | 'ss58_format': 42 39 | }, 40 | { 41 | # ecdsa 42 | 'address': '4pbsSkWcBaYoFHrKJZp5fDVUKbqSYD9dhZZGvpp3vQ5ysVs5ybV', 43 | 'public_key': '0x035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9', 44 | 'ss58_format': 200 45 | }, 46 | { 47 | 'address': 'yGF4JP7q5AK46d1FPCEm9sYQ4KooSjHMpyVAjLnsCSWVafPnf', 48 | 'public_key': '0x66cd6cf085627d6c85af1aaf2bd10cf843033e929b4e3b1c2ba8e4aa46fe111b', 49 | 'ss58_format': 255 50 | }, 51 | { 52 | 'address': 'yGDYxQatQwuxqT39Zs4LtcTnpzE12vXb7ZJ6xpdiHv6gTu1hF', 53 | 'public_key': '0x242fd5a078ac6b7c3c2531e9bcf1314343782aeb58e7bc6880794589e701db55', 54 | 'ss58_format': 255 55 | }, 56 | { 57 | 'address': 'mHm8k9Emsvyfp3piCauSH684iA6NakctF8dySQcX94GDdrJrE', 58 | 'public_key': '0x44d5a3ac156335ea99d33a83c57c7146c40c8e2260a8a4adf4e7a86256454651', 59 | 'ss58_format': 4242 60 | }, 61 | { 62 | 'address': 'r6Gr4gaMP8TsjhFbqvZhv3YvnasugLiRJpzpRHifsqqG18UXa', 63 | 'public_key': '0x88f01441682a17b52d6ae12d1a5670cf675fd254897efabaa5069eb3a701ab73', 64 | 'ss58_format': 14269 65 | } 66 | ] 67 | 68 | def test_encode_key_pair_alice_address(self): 69 | self.assertEqual(self.alice_keypair['address'], "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY") 70 | 71 | def test_encode_1_byte_account_index(self): 72 | self.assertEqual('F7NZ', ss58_encode_account_index(1)) 73 | 74 | def test_encode_1_byte_account_index_with_format(self): 75 | self.assertEqual('g4b', ss58_encode_account_index(1, ss58_format=2)) 76 | self.assertEqual('g4b', ss58_encode('0x01', ss58_format=2)) 77 | 78 | def test_encode_2_bytes_account_index(self): 79 | self.assertEqual('3xygo', ss58_encode_account_index(256, ss58_format=2)) 80 | self.assertEqual('3xygo', ss58_encode('0x0001', ss58_format=2)) 81 | 82 | def test_encode_4_bytes_account_index(self): 83 | self.assertEqual('zswfoZa', ss58_encode_account_index(67305985, ss58_format=2)) 84 | self.assertEqual('zswfoZa', ss58_encode('0x01020304', ss58_format=2)) 85 | 86 | def test_encode_8_bytes_account_index(self): 87 | self.assertEqual('848Gh2GcGaZia', ss58_encode('0x2a2c0a0000000000', ss58_format=2)) 88 | 89 | def test_decode_1_byte_account_index(self): 90 | self.assertEqual(1, ss58_decode_account_index('F7NZ')) 91 | 92 | def test_decode_2_bytes_account_index(self): 93 | self.assertEqual(256, ss58_decode_account_index('3xygo')) 94 | 95 | def test_decode_4_bytes_account_index(self): 96 | self.assertEqual(67305985, ss58_decode_account_index('zswfoZa')) 97 | 98 | def test_decode_8_bytes_account_index(self): 99 | self.assertEqual(666666, ss58_decode_account_index('848Gh2GcGaZia')) 100 | 101 | def test_encode_33_byte_address(self): 102 | self.assertEqual( 103 | 'KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou', 104 | ss58_encode('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077') 105 | ) 106 | 107 | def test_encode_with_2_byte_prefix(self): 108 | public_key = ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua') 109 | 110 | self.assertEqual( 111 | 'yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k', 112 | ss58_encode(public_key, ss58_format=255) 113 | ) 114 | 115 | def test_encode_subkey_generated_pairs(self): 116 | for subkey_pair in self.subkey_pairs: 117 | self.assertEqual( 118 | subkey_pair['address'], 119 | ss58_encode(address=subkey_pair['public_key'], ss58_format=subkey_pair['ss58_format']) 120 | ) 121 | 122 | def test_decode_subkey_generated_pairs(self): 123 | for subkey_pair in self.subkey_pairs: 124 | self.assertEqual( 125 | subkey_pair['public_key'], 126 | '0x' + ss58_decode(address=subkey_pair['address'], valid_ss58_format=subkey_pair['ss58_format']) 127 | ) 128 | 129 | def test_invalid_ss58_format_range_exceptions(self): 130 | with self.assertRaises(ValueError) as cm: 131 | ss58_encode(self.alice_keypair['public_key'], ss58_format=-1) 132 | self.assertEqual('Invalid value for ss58_format', str(cm.exception)) 133 | 134 | with self.assertRaises(ValueError) as cm: 135 | ss58_encode(self.alice_keypair['public_key'], ss58_format=16384) 136 | 137 | self.assertEqual('Invalid value for ss58_format', str(cm.exception)) 138 | 139 | def test_invalid_reserved_ss58_format(self): 140 | with self.assertRaises(ValueError) as cm: 141 | ss58_encode(self.alice_keypair['public_key'], ss58_format=46) 142 | 143 | self.assertEqual('Invalid value for ss58_format', str(cm.exception)) 144 | 145 | with self.assertRaises(ValueError) as cm: 146 | ss58_encode(self.alice_keypair['public_key'], ss58_format=47) 147 | 148 | self.assertEqual('Invalid value for ss58_format', str(cm.exception)) 149 | 150 | def test_invalid_public_key(self): 151 | with self.assertRaises(ValueError) as cm: 152 | ss58_encode(self.alice_keypair['public_key'][:30]) 153 | 154 | self.assertEqual('Invalid length for address', str(cm.exception)) 155 | 156 | def test_decode_public_key(self): 157 | self.assertEqual( 158 | '0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077', 159 | ss58_decode('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077') 160 | ) 161 | 162 | def test_decode_reserved_ss58_formats(self): 163 | 164 | with self.assertRaises(ValueError) as cm: 165 | ss58_decode('MGP3U1wqNhFofseKXU7B6FcZuLbvQvJFyin1EvQM65mBcNsY8') 166 | 167 | self.assertEqual('46 is a reserved SS58 format', str(cm.exception)) 168 | 169 | with self.assertRaises(ValueError) as cm: 170 | ss58_decode('MhvaLBvSb5jhjrftHLQPAvJegnpXgyDTE1ZprRNzAcfQSRdbL') 171 | 172 | self.assertEqual('47 is a reserved SS58 format', str(cm.exception)) 173 | 174 | def test_invalid_ss58_format_check(self): 175 | with self.assertRaises(ValueError) as cm: 176 | ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua', valid_ss58_format=2) 177 | 178 | self.assertEqual('Invalid SS58 format', str(cm.exception)) 179 | 180 | def test_decode_invalid_checksum(self): 181 | with self.assertRaises(ValueError) as cm: 182 | ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQub') 183 | 184 | self.assertEqual('Invalid checksum', str(cm.exception)) 185 | 186 | def test_decode_invalid_length(self): 187 | with self.assertRaises(ValueError) as cm: 188 | ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQubsdhfjksdhfkj') 189 | 190 | self.assertEqual('Invalid address length', str(cm.exception)) 191 | 192 | def test_decode_empty_string(self): 193 | with self.assertRaises(ValueError) as cm: 194 | ss58_decode('') 195 | 196 | self.assertEqual('Empty address provided', str(cm.exception)) 197 | 198 | def test_is_valid_ss58_address(self): 199 | self.assertTrue(is_valid_ss58_address('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')) 200 | self.assertTrue(is_valid_ss58_address('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', valid_ss58_format=42)) 201 | self.assertFalse(is_valid_ss58_address('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', valid_ss58_format=2)) 202 | 203 | self.assertTrue(is_valid_ss58_address('GLdQ4D4wkeEJUX8DBT9HkpycFVYQZ3fmJyQ5ZgBRxZ4LD3S', valid_ss58_format=2)) 204 | self.assertFalse(is_valid_ss58_address('GLdQ4D4wkeEJUX8DBT9HkpycFVYQZ3fmJyQ5ZgBRxZ4LD3S', valid_ss58_format=42)) 205 | self.assertFalse(is_valid_ss58_address('GLdQ4D4wkeEJUX8DBT9HkpycFVYQZ3fmJyQ5ZgBRxZ4LD3S', valid_ss58_format=0)) 206 | self.assertTrue(is_valid_ss58_address('12gX42C4Fj1wgtfgoP624zeHrcPBqzhb4yAENyvFdGX6EUnN', valid_ss58_format=0)) 207 | 208 | self.assertFalse(is_valid_ss58_address('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQ')) 209 | self.assertFalse(is_valid_ss58_address('6GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')) 210 | self.assertFalse(is_valid_ss58_address('0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d')) 211 | self.assertFalse(is_valid_ss58_address('d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d')) 212 | self.assertFalse(is_valid_ss58_address('incorrect_string')) 213 | 214 | 215 | if __name__ == '__main__': 216 | unittest.main() 217 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """A setuptools based setup module. 18 | 19 | See: 20 | https://packaging.python.org/guides/distributing-packages-using-setuptools/ 21 | https://github.com/pypa/sampleproject 22 | """ 23 | 24 | # Always prefer setuptools over distutils 25 | from setuptools import setup, find_packages 26 | from os import path, environ 27 | # io.open is needed for projects that support Python 2.7 28 | # It ensures open() defaults to text mode with universal newlines, 29 | # and accepts an argument to specify the text encoding 30 | # Python 3 only projects can skip this import 31 | from io import open 32 | 33 | 34 | if environ.get('TRAVIS_TAG'): 35 | version = environ['TRAVIS_TAG'].replace('v', '') 36 | elif environ.get('CI_COMMIT_TAG'): 37 | version = environ['CI_COMMIT_TAG'].replace('v', '') 38 | elif environ.get('GITHUB_REF'): 39 | 40 | if not environ['GITHUB_REF'].startswith('refs/tags/v'): 41 | raise ValueError('Incorrect tag format {}'.format(environ['GITHUB_REF'])) 42 | 43 | version = environ['GITHUB_REF'].replace('refs/tags/v', '') 44 | else: 45 | raise ValueError('Missing commit tag, can\'t set version') 46 | 47 | here = path.abspath(path.dirname(__file__)) 48 | 49 | # Get the long description from the README file 50 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 51 | long_description = f.read() 52 | 53 | # Arguments marked as "Required" below must be included for upload to PyPI. 54 | # Fields marked as "Optional" may be commented out. 55 | 56 | setup( 57 | # This is the name of your project. The first time you publish this 58 | # package, this name will be registered for you. It will determine how 59 | # users can install this project, e.g.: 60 | # 61 | # $ pip install sampleproject 62 | # 63 | # And where it will live on PyPI: https://pypi.org/project/sampleproject/ 64 | # 65 | # There are some restrictions on what makes a valid project name 66 | # specification here: 67 | # https://packaging.python.org/specifications/core-metadata/#name 68 | name='scalecodec', # Required 69 | 70 | # Versions should comply with PEP 440: 71 | # https://www.python.org/dev/peps/pep-0440/ 72 | # 73 | # For a discussion on single-sourcing the version across setup.py and the 74 | # project code, see 75 | # https://packaging.python.org/en/latest/single_source_version.html 76 | version=version, # Required 77 | 78 | # This is a one-line description or tagline of what your project does. This 79 | # corresponds to the "Summary" metadata field: 80 | # https://packaging.python.org/specifications/core-metadata/#summary 81 | description='Python SCALE Codec Library', # Optional 82 | 83 | # This is an optional longer description of your project that represents 84 | # the body of text which users will see when they visit PyPI. 85 | # 86 | # Often, this is the same as your README, so you can just read it in from 87 | # that file directly (as we have already done above) 88 | # 89 | # This field corresponds to the "Description" metadata field: 90 | # https://packaging.python.org/specifications/core-metadata/#description-optional 91 | long_description=long_description, # Optional 92 | 93 | # Denotes that our long_description is in Markdown; valid values are 94 | # text/plain, text/x-rst, and text/markdown 95 | # 96 | # Optional if long_description is written in reStructuredText (rst) but 97 | # required for plain-text or Markdown; if unspecified, "applications should 98 | # attempt to render [the long_description] as text/x-rst; charset=UTF-8 and 99 | # fall back to text/plain if it is not valid rst" (see link below) 100 | # 101 | # This field corresponds to the "Description-Content-Type" metadata field: 102 | # https://packaging.python.org/specifications/core-metadata/#description-content-type-optional 103 | long_description_content_type='text/markdown', # Optional (see note above) 104 | 105 | # This should be a valid link to your project's main homepage. 106 | # 107 | # This field corresponds to the "Home-Page" metadata field: 108 | # https://packaging.python.org/specifications/core-metadata/#home-page-optional 109 | url='https://github.com/polkascan/py-scale-codec', # Optional 110 | 111 | # This should be your name or the name of the organization which owns the 112 | # project. 113 | author='Stichting Polkascan (Polkascan Foundation)', # Optional 114 | 115 | # This should be a valid email address corresponding to the author listed 116 | # above. 117 | author_email='info@polkascan.org', # Optional 118 | 119 | # Classifiers help users find your project by categorizing it. 120 | # 121 | # For a list of valid classifiers, see https://pypi.org/classifiers/ 122 | classifiers=[ # Optional 123 | # How mature is this project? Common values are 124 | # 3 - Alpha 125 | # 4 - Beta 126 | # 5 - Production/Stable 127 | 'Development Status :: 5 - Production/Stable', 128 | 129 | # Indicate who your project is intended for 130 | 'Intended Audience :: Developers', 131 | 132 | # Pick your license as you wish 133 | 'License :: OSI Approved :: Apache Software License', 134 | 135 | # Specify the Python versions you support here. In particular, ensure 136 | # that you indicate whether you support Python 2, Python 3 or both. 137 | # These classifiers are *not* checked by 'pip install'. See instead 138 | # 'python_requires' below. 139 | 'Programming Language :: Python :: 3', 140 | 'Programming Language :: Python :: 3.6', 141 | 'Programming Language :: Python :: 3.7', 142 | 'Programming Language :: Python :: 3.8', 143 | 'Programming Language :: Python :: 3.9', 144 | ], 145 | 146 | # This field adds keywords for your project which will appear on the 147 | # project page. What does your project relate to? 148 | # 149 | # Note that this is a string of words separated by whitespace, not a list. 150 | keywords='scale codec polkascan polkadot substrate blockchain kusama', # Optional 151 | 152 | # You can just specify package directories manually here if your project is 153 | # simple. Or you can use find_packages(). 154 | # 155 | # Alternatively, if you just want to distribute a single Python file, use 156 | # the `py_modules` argument instead as follows, which will expect a file 157 | # called `my_module.py` to exist: 158 | # 159 | # py_modules=["my_module"], 160 | # 161 | #packages=find_packages(exclude=['contrib', 'docs', 'tests', 'test']), # Required 162 | packages=find_packages(exclude=['contrib', 'docs', 'tests', 'test']), # Required 163 | 164 | # Specify which Python versions you support. In contrast to the 165 | # 'Programming Language' classifiers above, 'pip install' will check this 166 | # and refuse to install the project if the version does not match. If you 167 | # do not support Python 2, you can simplify this to '>=3.5' or similar, see 168 | # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires 169 | 170 | #python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4', 171 | python_requires='>=3.6, <4', 172 | 173 | # This field lists other packages that your project depends on to run. 174 | # Any package you put here will be installed by pip when your project is 175 | # installed, so they must be valid existing projects. 176 | # 177 | # For an analysis of "install_requires" vs pip's requirements files see: 178 | # https://packaging.python.org/en/latest/requirements.html 179 | install_requires=['more-itertools', 'base58>=2.0.1', 'requests>=2.24.0'], # Optional 180 | 181 | # List additional groups of dependencies here (e.g. development 182 | # dependencies). Users will be able to install these using the "extras" 183 | # syntax, for example: 184 | # 185 | # $ pip install sampleproject[dev] 186 | # 187 | # Similar to `install_requires` above, these must be valid existing 188 | # projects. 189 | extras_require={ # Optional 190 | #'dev': ['check-manifest'], 191 | 'test': ['coverage', 'pytest'], 192 | }, 193 | 194 | # If there are data files included in your packages that need to be 195 | # installed, specify them here. 196 | # 197 | # If using Python 2.6 or earlier, then these have to be included in 198 | # MANIFEST.in as well. 199 | 200 | package_data={ # Optional 201 | 'scalecodec.type_registry': ['*.json'], 202 | }, 203 | 204 | # Although 'package_data' is the preferred approach, in some case you may 205 | # need to place data files outside of your packages. See: 206 | # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files 207 | # 208 | # In this case, 'data_file' will be installed into '/my_data' 209 | # data_files=[('type_registry', ['scalecodec/type_registry/*.json'])], # Optional 210 | 211 | # To provide executable scripts, use entry points in preference to the 212 | # "scripts" keyword. Entry points provide cross-platform support and allow 213 | # `pip` to create the appropriate form of executable for the target 214 | # platform. 215 | # 216 | # For example, the following would provide a command called `sample` which 217 | # executes the function `main` from this package when invoked: 218 | 219 | # entry_points={ # Optional 220 | # 'console_scripts': [ 221 | # 'sample=sample:main', 222 | # ], 223 | # }, 224 | 225 | # List additional URLs that are relevant to your project as a dict. 226 | # 227 | # This field corresponds to the "Project-URL" metadata fields: 228 | # https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use 229 | # 230 | # Examples listed include a pattern for specifying where the package tracks 231 | # issues, where the source is hosted, where to say thanks to the package 232 | # maintainers, and where to support the project financially. The key is 233 | # what's used to render the link text on PyPI. 234 | # project_urls={ # Optional 235 | # 'Bug Reports': 'https://github.com/pypa/sampleproject/issues', 236 | # 'Funding': 'https://donate.pypi.org', 237 | # 'Say Thanks!': 'http://saythanks.io/to/example', 238 | # 'Source': 'https://github.com/pypa/sampleproject/', 239 | # }, 240 | ) 241 | -------------------------------------------------------------------------------- /test/test_scale_info.py: -------------------------------------------------------------------------------- 1 | # Polkascan Substrate Interface GUI 2 | # 3 | # Copyright 2018-2020 openAware BV (NL). 4 | # This file is part of Polkascan. 5 | # 6 | # Polkascan is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Polkascan is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Polkascan. If not, see . 18 | # 19 | # test_scale_info.py 20 | # 21 | import os 22 | import unittest 23 | 24 | from scalecodec.types import GenericAccountId, Null 25 | 26 | from scalecodec.base import RuntimeConfigurationObject, ScaleDecoder, ScaleBytes 27 | 28 | from scalecodec.type_registry import load_type_registry_file, load_type_registry_preset 29 | 30 | 31 | class ScaleInfoTestCase(unittest.TestCase): 32 | 33 | @classmethod 34 | def setUpClass(cls): 35 | module_path = os.path.dirname(__file__) 36 | 37 | # scale_info_defaults = load_type_registry_file(os.path.join(module_path, 'fixtures', 'scale_info_defaults.json')) 38 | 39 | cls.runtime_config = RuntimeConfigurationObject(ss58_format=42) 40 | cls.runtime_config.update_type_registry(load_type_registry_preset("core")) 41 | # cls.runtime_config.update_type_registry(scale_info_defaults) 42 | 43 | cls.metadata_fixture_dict = load_type_registry_file( 44 | os.path.join(module_path, 'fixtures', 'metadata_hex.json') 45 | ) 46 | 47 | cls.metadata_obj = cls.runtime_config.create_scale_object( 48 | 'MetadataVersioned', data=ScaleBytes(cls.metadata_fixture_dict['V14']) 49 | ) 50 | cls.metadata_obj.decode() 51 | 52 | cls.runtime_config.add_portable_registry(cls.metadata_obj) 53 | 54 | def test_path_overrides(self): 55 | account_cls = self.runtime_config.get_decoder_class('scale_info::0') 56 | self.assertIsInstance(account_cls(), GenericAccountId) 57 | 58 | def test_primitives(self): 59 | # scale_info::2 = u8 60 | obj = self.runtime_config.create_scale_object( 61 | 'scale_info::2', ScaleBytes("0x02") 62 | ) 63 | obj.decode() 64 | self.assertEqual(obj.value, 2) 65 | 66 | # scale_info::4 = u32 67 | obj = self.runtime_config.create_scale_object( 68 | 'scale_info::4', ScaleBytes("0x2efb0000") 69 | ) 70 | obj.decode() 71 | self.assertEqual(obj.value, 64302) 72 | 73 | def test_compact(self): 74 | # scale_info::98 = compact 75 | obj = self.runtime_config.create_scale_object( 76 | 'scale_info::98', ScaleBytes("0x02093d00") 77 | ) 78 | obj.decode() 79 | self.assertEqual(obj.value, 1000000) 80 | 81 | # scale_info::63 = compact 82 | obj = self.runtime_config.create_scale_object( 83 | 'scale_info::63', ScaleBytes("0x130080cd103d71bc22") 84 | ) 85 | obj.decode() 86 | self.assertEqual(obj.value, 2503000000000000000) 87 | 88 | def test_array(self): 89 | # scale_info::14 = [u8; 4] 90 | obj = self.runtime_config.create_scale_object( 91 | 'scale_info::14', ScaleBytes("0x01020304"), 92 | 93 | ) 94 | obj.decode() 95 | self.assertEqual(obj.value, "0x01020304") 96 | 97 | def test_enum(self): 98 | # ['sp_runtime', 'generic', 'digest', 'DigestItem'] 99 | obj = self.runtime_config.create_scale_object( 100 | 'sp_runtime::generic::digest::DigestItem', ScaleBytes("0x001054657374") 101 | ) 102 | obj.decode() 103 | self.assertEqual({"Other": "Test"}, obj.value) 104 | 105 | obj.encode({'Other': "Test"}) 106 | self.assertEqual(obj.data.to_hex(), "0x001054657374") 107 | 108 | def test_enum_multiple_fields(self): 109 | 110 | obj = self.runtime_config.create_scale_object( 111 | 'sp_runtime::generic::digest::DigestItem', ScaleBytes("0x06010203041054657374") 112 | ) 113 | obj.decode() 114 | 115 | self.assertEqual({'PreRuntime': ("0x01020304", "Test")}, obj.value) 116 | 117 | data = obj.encode({'PreRuntime': ("0x01020304", "Test")}) 118 | self.assertEqual("0x06010203041054657374", data.to_hex()) 119 | 120 | def test_enum_no_value(self): 121 | obj = self.runtime_config.create_scale_object( 122 | 'scale_info::21', ScaleBytes("0x02") 123 | ) 124 | obj.decode() 125 | self.assertEqual('CodeUpdated', obj.value) 126 | 127 | def test_named_struct(self): 128 | # scale_info::111 = ['frame_support', 'weights', 'RuntimeDbWeight'] 129 | obj = self.runtime_config.create_scale_object( 130 | 'scale_info::111', 131 | ScaleBytes("0xe110000000000000d204000000000000") 132 | ) 133 | obj.decode() 134 | 135 | self.assertEqual(obj.value, { 136 | 'read': 4321, 137 | 'write': 1234 138 | }) 139 | 140 | obj.encode({ 141 | 'read': 4321, 142 | 'write': 1234 143 | }) 144 | 145 | self.assertEqual(obj.data.to_hex(), '0xe110000000000000d204000000000000') 146 | 147 | def test_unnamed_struct_one_element(self): 148 | # ('sp_arithmetic::per_things::percent', ) 149 | obj = self.runtime_config.create_scale_object( 150 | 'scale_info::203', 151 | ScaleBytes("0x04") 152 | ) 153 | obj.decode() 154 | self.assertEqual(obj.value, 4) 155 | 156 | obj.encode(5) 157 | self.assertEqual(obj.data.to_hex(), "0x05") 158 | 159 | def test_unnamed_struct_multiple_elements(self): 160 | # pallet_democracy::vote::PriorLock 161 | obj = self.runtime_config.create_scale_object( 162 | 'scale_info::377', 163 | ScaleBytes("0x0c00000022000000000000000000000000000000") 164 | ) 165 | 166 | obj.decode() 167 | self.assertEqual((12, 34), obj.value) 168 | 169 | data = obj.encode((12, 34)) 170 | self.assertEqual(data.to_hex(), '0x0c00000022000000000000000000000000000000') 171 | 172 | def test_tuple(self): 173 | obj = self.runtime_config.create_scale_object( 174 | 'scale_info::73', 175 | ScaleBytes("0x0400000003000000") 176 | ) 177 | obj.decode() 178 | 179 | self.assertEqual((4, 3), obj.value) 180 | 181 | def test_option_none(self): 182 | obj = self.runtime_config.create_scale_object( 183 | 'scale_info::74', 184 | ScaleBytes("0x00") 185 | ) 186 | obj.decode() 187 | 188 | self.assertIsNone(obj.value) 189 | 190 | data = obj.encode(None) 191 | 192 | self.assertEqual('0x00', data.to_hex()) 193 | 194 | def test_option_some(self): 195 | obj = self.runtime_config.create_scale_object( 196 | 'scale_info::35', 197 | ScaleBytes("0x0101") 198 | ) 199 | obj.decode() 200 | self.assertEqual('Signed', obj.value) 201 | 202 | data = obj.encode('OnChain') 203 | self.assertEqual(data.to_hex(), '0x0100') 204 | 205 | def test_weak_bounded_vec(self): 206 | # 87 = ['frame_support', 'storage', 'weak_bounded_vec', 'WeakBoundedVec'] 207 | obj = self.runtime_config.create_scale_object( 208 | 'scale_info::318', 209 | ScaleBytes("0x0401020304050607080a00000000000000000000000000000000") 210 | ) 211 | obj.decode() 212 | 213 | self.assertEqual([{"id": "0x0102030405060708", 'amount': 10, 'reasons': "Fee"}], obj.value) 214 | 215 | data = obj.encode([{"id": "0x0102030405060708", 'amount': 10, 'reasons': "Fee"}]) 216 | self.assertEqual('0x0401020304050607080a00000000000000000000000000000000', data.to_hex()) 217 | 218 | def test_bounded_vec(self): 219 | # 'scale_info::90' = frame_support::storage::bounded_vec::BoundedVec 220 | obj = self.runtime_config.create_scale_object( 221 | 'scale_info::90', 222 | ScaleBytes("0x084345") 223 | ) 224 | 225 | obj.decode() 226 | 227 | self.assertEqual('CE', obj.value) 228 | 229 | data = obj.encode([67, 69]) 230 | self.assertEqual('0x084345', data.to_hex()) 231 | 232 | data = obj.encode('CE') 233 | self.assertEqual('0x084345', data.to_hex()) 234 | 235 | def test_data(self): 236 | # 'scale_info::247' = pallet_identity::types::data 237 | obj = self.runtime_config.create_scale_object( 238 | 'pallet_identity::types::data', 239 | ScaleBytes("0x065465737431") 240 | ) 241 | obj.decode() 242 | 243 | self.assertEqual({"Raw": "Test1"}, obj.value) 244 | 245 | data = obj.encode({"Raw": "Test123"}) 246 | self.assertEqual('0x0854657374313233', data.to_hex()) 247 | 248 | def test_era(self): 249 | # 'scale_info::516' = sp_runtime::generic::era::era 250 | obj = self.runtime_config.create_scale_object( 251 | 'scale_info::516', 252 | ScaleBytes("0x4e9c") 253 | ) 254 | obj.decode() 255 | 256 | self.assertTupleEqual(obj.value, (32768, 20000)) 257 | self.assertEqual(obj.period, 32768) 258 | self.assertEqual(obj.phase, 20000) 259 | self.assertFalse(obj.is_immortal()) 260 | 261 | def test_multiaddress(self): 262 | # 'scale_info::139' = sp_runtime::multiaddress::MultiAddress 263 | obj = self.runtime_config.create_scale_object( 264 | 'sp_runtime::multiaddress::MultiAddress', 265 | ScaleBytes("0x00d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d") 266 | ) 267 | 268 | obj.decode() 269 | self.assertEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', obj.value) 270 | self.assertEqual('d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d', obj.account_id) 271 | 272 | data = obj.encode({'Id': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'}) 273 | self.assertEqual(ScaleBytes('0x00d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'), data) 274 | self.assertEqual('d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d', obj.account_id) 275 | 276 | def test_unknown_scale_info_type(self): 277 | 278 | unknown_type = self.runtime_config.create_scale_object('RegistryType') 279 | 280 | unknown_type.value = { 281 | 'path': [], 282 | 'params': [], 283 | 'def': 'unknown', 284 | 'docs': [] 285 | } 286 | 287 | with self.assertRaises(NotImplementedError): 288 | self.runtime_config.get_decoder_class_for_scale_info_definition('unknown::type', unknown_type, 'runtime') 289 | 290 | def test_encode_call(self): 291 | call = self.runtime_config.create_scale_object( 292 | "Call", metadata=self.metadata_obj 293 | ) 294 | call.encode({ 295 | "call_module": "Balances", 296 | "call_function": "transfer", 297 | "call_args": {"dest": "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", "value": 3}, 298 | }) 299 | self.assertEqual( 300 | call.data.to_hex(), 301 | '0x060000be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f0c' 302 | ) 303 | 304 | 305 | if __name__ == '__main__': 306 | unittest.main() 307 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /scalecodec/type_registry/kusama.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime_id": 9122, 3 | "types": { 4 | "Address": "MultiAddress", 5 | "LookupSource": "MultiAddress", 6 | "AccountInfo": "AccountInfoWithTripleRefCount", 7 | "BlockNumber": "U32", 8 | "LeasePeriod": "BlockNumber", 9 | "Weight": "WeightV1", 10 | "Keys": { 11 | "type": "struct", 12 | "type_mapping": [ 13 | ["grandpa", "AccountId"], 14 | ["babe", "AccountId"], 15 | ["im_online", "AccountId"], 16 | ["para_validator", "AccountId"], 17 | ["para_assignment", "AccountId"], 18 | ["authority_discovery", "AccountId"] 19 | ] 20 | }, 21 | "DispatchInfo": { 22 | "type": "struct", 23 | "type_mapping": [ 24 | ["weight", "Weight"], 25 | ["class", "DispatchClass"], 26 | ["paysFee", "Pays"] 27 | ] 28 | }, 29 | "ProxyType": { 30 | "type": "enum", 31 | "value_list": [ 32 | "Any", 33 | "NonTransfer", 34 | "Governance", 35 | "Staking", 36 | "IdentityJudgement", 37 | "CancelProxy" 38 | ] 39 | }, 40 | "RefCount": "u32", 41 | "ValidatorPrefs": "ValidatorPrefsWithBlocked", 42 | "CompactAssignments": "CompactAssignmentsWith24", 43 | "RawSolution": "RawSolutionWith24", 44 | "AssetInstance": "AssetInstanceV0", 45 | "MultiAsset": "MultiAssetV0", 46 | "MultiLocation": "MultiLocationV0", 47 | "Response": "ResponseV0", 48 | "Xcm": "XcmV0", 49 | "XcmOrder": "XcmOrderV0" 50 | }, 51 | "versioning": [ 52 | { 53 | "runtime_range": [1019, 1031], 54 | "types": { 55 | "DispatchError": { 56 | "type": "struct", 57 | "type_mapping": [ 58 | ["module", "Option"], 59 | ["error", "u8"] 60 | ] 61 | } 62 | } 63 | }, 64 | { 65 | "runtime_range": [1032, null], 66 | "types": { 67 | "DispatchError": { 68 | "type": "enum", 69 | "type_mapping": [ 70 | ["Other", "Null"], 71 | ["CannotLookup", "Null"], 72 | ["BadOrigin", "Null"], 73 | ["Module", "DispatchErrorModule"] 74 | ] 75 | } 76 | } 77 | }, 78 | { 79 | "runtime_range": [1019, 1037], 80 | "types": { 81 | "IdentityInfo": { 82 | "type": "struct", 83 | "type_mapping": [ 84 | ["additional", "Vec"], 85 | ["display", "Data"], 86 | ["legal", "Data"], 87 | ["web", "Data"], 88 | ["riot", "Data"], 89 | ["email", "Data"], 90 | ["pgp_fingerprint", "Option"], 91 | ["image", "Data"] 92 | ] 93 | } 94 | } 95 | }, 96 | { 97 | "runtime_range": [1038, null], 98 | "types": { 99 | "IdentityInfo": { 100 | "type": "struct", 101 | "type_mapping": [ 102 | ["additional", "Vec"], 103 | ["display", "Data"], 104 | ["legal", "Data"], 105 | ["web", "Data"], 106 | ["riot", "Data"], 107 | ["email", "Data"], 108 | ["pgp_fingerprint", "Option"], 109 | ["image", "Data"], 110 | ["twitter", "Data"] 111 | ] 112 | } 113 | } 114 | }, 115 | { 116 | "runtime_range": [1019, 1042], 117 | "types": { 118 | "SlashingSpans": { 119 | "type": "struct", 120 | "type_mapping": [ 121 | ["span_index", "SpanIndex"], 122 | ["last_start", "EraIndex"], 123 | ["prior", "Vec"] 124 | ] 125 | } 126 | } 127 | }, 128 | { 129 | "runtime_range": [1043, null], 130 | "types": { 131 | "SlashingSpans": { 132 | "type": "struct", 133 | "type_mapping": [ 134 | ["span_index", "SpanIndex"], 135 | ["last_start", "EraIndex"], 136 | ["last_nonzero_slash", "EraIndex"], 137 | ["prior", "Vec"] 138 | ] 139 | } 140 | } 141 | }, 142 | { 143 | "runtime_range": [1019, 1045], 144 | "types": { 145 | "StakingLedger": "StakingLedgerTo223", 146 | "BalanceLock": { 147 | "type": "struct", 148 | "type_mapping": [ 149 | ["id", "LockIdentifier"], 150 | ["amount", "Balance"], 151 | ["until", "BlockNumber"], 152 | ["reasons", "WithdrawReasons"] 153 | ] 154 | } 155 | } 156 | }, 157 | { 158 | "runtime_range": [1050, 1056], 159 | "types": { 160 | "StakingLedger": "StakingLedgerTo240", 161 | "BalanceLock": { 162 | "type": "struct", 163 | "type_mapping": [ 164 | ["id", "LockIdentifier"], 165 | ["amount", "Balance"], 166 | ["reasons", "Reasons"] 167 | ] 168 | } 169 | } 170 | }, 171 | { 172 | "runtime_range": [1057, null], 173 | "types": { 174 | "StakingLedger": { 175 | "type": "struct", 176 | "type_mapping": [ 177 | [ 178 | "stash", 179 | "AccountId" 180 | ], 181 | [ 182 | "total", 183 | "Compact" 184 | ], 185 | [ 186 | "active", 187 | "Compact" 188 | ], 189 | [ 190 | "unlocking", 191 | "Vec" 192 | ], 193 | [ 194 | "claimed_rewards", 195 | "Vec" 196 | ] 197 | ] 198 | }, 199 | "BalanceLock": { 200 | "type": "struct", 201 | "type_mapping": [ 202 | ["id", "LockIdentifier"], 203 | ["amount", "Balance"], 204 | ["reasons", "Reasons"] 205 | ] 206 | } 207 | } 208 | }, 209 | { 210 | "runtime_range": [1019, 1054], 211 | "types": { 212 | "ReferendumInfo": { 213 | "type": "struct", 214 | "type_mapping": [ 215 | ["end", "BlockNumber"], 216 | ["proposal", "Proposal"], 217 | ["threshold", "VoteThreshold"], 218 | ["delay", "BlockNumber"] 219 | ] 220 | } 221 | } 222 | }, 223 | { 224 | "runtime_range": [1054, null], 225 | "types": { 226 | "ReferendumInfo": { 227 | "type": "enum", 228 | "type_mapping": [ 229 | ["Ongoing", "ReferendumStatus"], 230 | ["Finished", "ReferendumInfoFinished"] 231 | ] 232 | } 233 | } 234 | }, 235 | { 236 | "runtime_range": [1019, 1056], 237 | "types": { 238 | "WeightV1": "u32", 239 | "Weight": "WeightV1" 240 | } 241 | }, 242 | { 243 | "runtime_range": [1057, null], 244 | "types": { 245 | "WeightV1": "u64", 246 | "Weight": "WeightV1" 247 | } 248 | }, 249 | { 250 | "runtime_range": [1019, 1061], 251 | "types": { 252 | "Heartbeat": { 253 | "type": "struct", 254 | "type_mapping": [ 255 | ["block_number", "BlockNumber"], 256 | ["network_state", "OpaqueNetworkState"], 257 | ["session_index", "SessionIndex"], 258 | ["authority_index", "AuthIndex"] 259 | ] 260 | }, 261 | "DispatchInfo": { 262 | "type": "struct", 263 | "type_mapping": [ 264 | ["weight", "Weight"], 265 | ["class", "DispatchClass"], 266 | ["pays_fee", "bool"] 267 | ] 268 | } 269 | } 270 | }, 271 | { 272 | "runtime_range": [1062, null], 273 | "types": { 274 | "Heartbeat": { 275 | "type": "struct", 276 | "type_mapping": [ 277 | ["block_number", "BlockNumber"], 278 | ["network_state", "OpaqueNetworkState"], 279 | ["session_index", "SessionIndex"], 280 | ["authority_index", "AuthIndex"], 281 | ["validators_len", "u32"] 282 | ] 283 | }, 284 | "DispatchInfo": { 285 | "type": "struct", 286 | "type_mapping": [ 287 | ["weight", "Weight"], 288 | ["class", "DispatchClass"], 289 | ["pays_fee", "Pays"] 290 | ] 291 | } 292 | } 293 | }, 294 | { 295 | "runtime_range": [1019, 2012], 296 | "types": { 297 | "OpenTip": { 298 | "type": "struct", 299 | "type_mapping": [ 300 | ["reason", "Hash"], 301 | ["who", "AccountId"], 302 | ["finder", "Option"], 303 | ["closes", "Option"], 304 | ["tips", "Vec"] 305 | ] 306 | } 307 | } 308 | }, 309 | { 310 | "runtime_range": [2013, null], 311 | "types": { 312 | "OpenTip": { 313 | "type": "struct", 314 | "type_mapping": [ 315 | ["reason", "Hash"], 316 | ["who", "AccountId"], 317 | ["finder", "AccountId"], 318 | ["deposit", "Balance"], 319 | ["closes", "Option"], 320 | ["tips", "Vec"], 321 | ["finders_fee", "bool"] 322 | ] 323 | } 324 | } 325 | }, 326 | { 327 | "runtime_range": [1019, 2022], 328 | "types": { 329 | "CompactAssignments": "CompactAssignmentsTo257" 330 | } 331 | }, 332 | { 333 | "runtime_range": [2023, 9009], 334 | "types": { 335 | "CompactAssignments": "CompactAssignmentsWith16", 336 | "RawSolution": "RawSolutionWith16" 337 | } 338 | }, 339 | { 340 | "runtime_range": [9010, null], 341 | "types": { 342 | "CompactAssignments": "CompactAssignmentsWith24", 343 | "RawSolution": "RawSolutionWith24" 344 | } 345 | }, 346 | { 347 | "runtime_range": [1019, 2024], 348 | "types": { 349 | "RefCount": "u8" 350 | } 351 | }, 352 | { 353 | "runtime_range": [2025, null], 354 | "types": { 355 | "RefCount": "u32" 356 | } 357 | }, 358 | { 359 | "runtime_range": [1019, 1045], 360 | "types": { 361 | "Address": "RawAddress", 362 | "LookupSource": "RawAddress", 363 | "AccountInfo": "AccountInfoWithRefCount", 364 | "Keys": { 365 | "type": "struct", 366 | "type_mapping": [ 367 | ["grandpa", "AccountId"], 368 | ["babe", "AccountId"], 369 | ["im_online", "AccountId"], 370 | ["authority_discovery", "AccountId"], 371 | ["parachains", "AccountId"] 372 | ] 373 | }, 374 | "ValidatorPrefs": "ValidatorPrefsWithCommission" 375 | } 376 | }, 377 | { 378 | "runtime_range": [1050, 2027], 379 | "types": { 380 | "Address": "AccountIdAddress", 381 | "LookupSource": "AccountIdAddress", 382 | "AccountInfo": "AccountInfoWithRefCount", 383 | "Keys": { 384 | "type": "struct", 385 | "type_mapping": [ 386 | ["grandpa", "AccountId"], 387 | ["babe", "AccountId"], 388 | ["im_online", "AccountId"], 389 | ["authority_discovery", "AccountId"], 390 | ["parachains", "AccountId"] 391 | ] 392 | }, 393 | "ValidatorPrefs": "ValidatorPrefsWithCommission" 394 | } 395 | }, 396 | { 397 | "runtime_range": [2028, null], 398 | "types": { 399 | "Address": "MultiAddress", 400 | "LookupSource": "MultiAddress", 401 | "Keys": { 402 | "type": "struct", 403 | "type_mapping": [ 404 | ["grandpa", "AccountId"], 405 | ["babe", "AccountId"], 406 | ["im_online", "AccountId"], 407 | ["para_validator", "AccountId"], 408 | ["para_assignment", "AccountId"], 409 | ["authority_discovery", "AccountId"] 410 | ] 411 | }, 412 | "ValidatorPrefs": "ValidatorPrefsWithBlocked" 413 | } 414 | }, 415 | { 416 | "runtime_range": [2028, 2029], 417 | "types": { 418 | "AccountInfo": "AccountInfoWithDualRefCount" 419 | } 420 | }, 421 | { 422 | "runtime_range": [2030, null], 423 | "types": { 424 | "AccountInfo": "AccountInfoWithTripleRefCount" 425 | } 426 | }, 427 | { 428 | "runtime_range": [9010, null], 429 | "types": { 430 | "AssetInstance": "AssetInstanceV0", 431 | "MultiAsset": "MultiAssetV0", 432 | "MultiLocation": "MultiLocationV0", 433 | "Response": "ResponseV0", 434 | "Xcm": "XcmV0", 435 | "XcmOrder": "XcmOrderV0" 436 | } 437 | } 438 | ], 439 | "runtime_upgrades": [ 440 | [0, 1020], [26669, 1021], [38245, 1022], [54248, 1023], [59659, 1024], 441 | [67651, 1025], [82191, 1027], [83238, 1028], [101503, 1029], [203466, 1030], 442 | [295787, 1031], [461692, 1032], [504329, 1033], [569327, 1038], [587687, 1039], 443 | [653183, 1040], [693488, 1042], [901442, 1045], [1375086, 1050], [1445458, 1051], 444 | [1472960, 1052], [1475648, 1053], [1491596, 1054], [1574408, 1055], [2064961, 1058], 445 | [2201991, 1062], [2671528, 2005], [2704202, 2007], [2728002, 2008], [2832534, 2011], 446 | [2962294, 2012], [3240000, 2013], [3274408, 2015], [3323565, 2019], [3534175, 2022], 447 | [3860281, 2023], [4143129, 2024], [4401242, 2025], [4841367, 2026], [5961600, 2027], 448 | [6137912, 2028], [6561855, 2029], [7100891, 2030], [7468792, 9010], [7668600, 9030], 449 | [7812476, 9040], [8010981, 9050], [8073833, 9070], [8555825, 9080], [8945245, 9090] 450 | ] 451 | } 452 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | [Substrate](https://github.com/paritytech/substrate) uses a lightweight and efficient [encoding and decoding program](https://docs.substrate.io/reference/scale-codec/) to optimize how data is sent and received over the network. The program used to serialize and deserialize data is called the SCALE codec, with SCALE being an acronym for **S**imple **C**oncatenated **A**ggregate **L**ittle-**E**ndian. 3 | 4 | ## Installation 5 | ```bash 6 | pip install scalecodec 7 | ``` 8 | 9 | ## Examples of different types 10 | 11 | | Type | Description | Example SCALE decoding value | SCALE encoded value | 12 | |------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------------| 13 | | `bool` | Boolean values are encoded using the least significant bit of a single byte. | `True` | `0x01` | 14 | | `u16` | Basic integers are encoded using a fixed-width little-endian (LE) format. | `42` | `0x2a00` | 15 | | `Compact` | A "compact" or general integer encoding is sufficient for encoding large integers (up to 2**536) and is more efficient at encoding most values than the fixed-width version. (Though for single-byte values, the fixed-width integer is never worse.) | `0` | `0x00` | 16 | | | | `1` | `0x04` | 17 | | | | `42` | `0xa8` | 18 | | | | `69` | `0x1501` | 19 | | | | `100000000000000` | `0x0b00407a10f35a` | 20 | | `Vec` | A collection of same-typed values is encoded, prefixed with a compact encoding of the number of items, followed by each item's encoding concatenated in turn. | `[4, 8, 15, 16, 23, 42]` | `0x18040008000f00100017002a00` | 21 | | `BitVec` | A sequence of bools, represented in a more space efficient bit format | `0b00000010_01111101` | `0x287d02` | 22 | | `str`,`Bytes`, `String` | Strings are Vectors of bytes (`Vec`) containing a valid UTF8 sequence. | `"Test"` | `0x1054657374` | 23 | | | | `b"Test"` | `0x1054657374` | 24 | | | | `[84, 101, 115, 116]` | `0x1054657374` | 25 | | `[u8; 4]` | Fixed sized array of in this case an `u8` | `b"babe"` | `0x62616265` | 26 | | | | `"0x62616265"` | `0x62616265` | 27 | | | | `[98, 97, 98, 101]` | `0x62616265` | 28 | | `AccountId` | An [SS58 formatted](https://docs.substrate.io/reference/address-formats/) representation of an account. See also the [SS58 util functions](https://polkascan.github.io/py-scale-codec/utils/ss58.html) | `"5GDyPHLVHcQYPTWfygtPY eogQjyZy7J9fsi4brPhgEFq4pcv"` | `0xb80269ec500e458a630846b99105c397 ee574125823d6f4388e9c7572e115c05` | 29 | | `Enum` Example: `enum IntOrBool { Int(u8), Bool(bool),}` | A fixed number of variants, each mutually exclusive and potentially implying a further value or series of values. Encoded as the first byte identifying the index of the variant that the value is. Any further bytes are used to encode any data that the variant implies. Thus, no more than 256 variants are supported. | `{'Int': 8}` | `0x002a` | 30 | | | | `{'Bool': True}` | `0x0101` | 31 | | `Struct` Example: `struct Motion { pub votes: Vec, pub id: u32 }` | For structures, the values are named, but that is irrelevant for the encoding (names are ignored - only order matters). All containers store elements consecutively. The order of the elements is not fixed, depends on the container, and cannot be relied on at decoding. This implicitly means that decoding some byte-array into a specified structure that enforces an order and then re-encoding it could result in a different byte array than the original that was decoded. | `{"votes": ["5GDyPHLVHcQYPTWfygtPYeo gQjyZy7J9fsi4brPhgEFq4pcv"], "id": 4}` | `0x04b80269ec500e458a630846b99105c397ee57 4125823d6f4388e9c7572e115c0504000000` | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec 2 | 3 | [![Build Status](https://img.shields.io/github/actions/workflow/status/polkascan/py-scale-codec/unittests.yml?branch=master)](https://github.com/polkascan/py-scale-codec/actions/workflows/unittests.yml?query=workflow%3A%22Run+unit+tests%22) 4 | [![Latest Version](https://img.shields.io/pypi/v/scalecodec.svg)](https://pypi.org/project/scalecodec/) 5 | [![Supported Python versions](https://img.shields.io/pypi/pyversions/scalecodec.svg)](https://pypi.org/project/scalecodec/) 6 | [![License](https://img.shields.io/pypi/l/scalecodec.svg)](https://github.com/polkascan/py-scale-codec/blob/master/LICENSE) 7 | 8 | 9 | ## Description 10 | [Substrate](https://github.com/paritytech/substrate) uses a lightweight and efficient [encoding and decoding program](https://docs.substrate.io/reference/scale-codec/) to optimize how data is sent and received over the network. The program used to serialize and deserialize data is called the SCALE codec, with SCALE being an acronym for **S**imple **C**oncatenated **A**ggregate **L**ittle-**E**ndian. 11 | 12 | ## Documentation 13 | https://polkascan.github.io/py-scale-codec/ 14 | 15 | 16 | ## Installation 17 | ```bash 18 | pip install scalecodec 19 | ``` 20 | 21 | ## Examples of different types 22 | 23 | | Type | Description | Example SCALE decoding value | SCALE encoded value | 24 | |------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------------| 25 | | `bool` | Boolean values are encoded using the least significant bit of a single byte. | `True` | `0x01` | 26 | | `u16` | Basic integers are encoded using a fixed-width little-endian (LE) format. | `42` | `0x2a00` | 27 | | `Compact` | A "compact" or general integer encoding is sufficient for encoding large integers (up to 2**536) and is more efficient at encoding most values than the fixed-width version. (Though for single-byte values, the fixed-width integer is never worse.) | `0` | `0x00` | 28 | | | | `1` | `0x04` | 29 | | | | `42` | `0xa8` | 30 | | | | `69` | `0x1501` | 31 | | | | `100000000000000` | `0x0b00407a10f35a` | 32 | | `Vec` | A collection of same-typed values is encoded, prefixed with a compact encoding of the number of items, followed by each item's encoding concatenated in turn. | `[4, 8, 15, 16, 23, 42]` | `0x18040008000f00100017002a00` | 33 | | `BitVec` | A sequence of bools, represented in a more space efficient bit format | `0b00000010_01111101` | `0x287d02` | 34 | | `str`,`Bytes`, `String` | Strings are Vectors of bytes (`Vec`) containing a valid UTF8 sequence. | `"Test"` | `0x1054657374` | 35 | | | | `b"Test"` | `0x1054657374` | 36 | | | | `[84, 101, 115, 116]` | `0x1054657374` | 37 | | `[u8; 4]` | Fixed sized array of in this case an `u8` | `b"babe"` | `0x62616265` | 38 | | | | `"0x62616265"` | `0x62616265` | 39 | | | | `[98, 97, 98, 101]` | `0x62616265` | 40 | | `AccountId` | An [SS58 formatted](https://docs.substrate.io/reference/address-formats/) representation of an account. See also the [SS58 util functions](https://polkascan.github.io/py-scale-codec/utils/ss58.html) | `"5GDyPHLVHcQYPTWfygtPY eogQjyZy7J9fsi4brPhgEFq4pcv"` | `0xb80269ec500e458a630846b99105c397 ee574125823d6f4388e9c7572e115c05` | 41 | | `Enum` Example: `enum IntOrBool { Int(u8), Bool(bool),}` | A fixed number of variants, each mutually exclusive and potentially implying a further value or series of values. Encoded as the first byte identifying the index of the variant that the value is. Any further bytes are used to encode any data that the variant implies. Thus, no more than 256 variants are supported. | `{'Int': 8}` | `0x002a` | 42 | | | | `{'Bool': True}` | `0x0101` | 43 | | `Struct` Example: `struct Motion { pub votes: Vec, pub id: u32 }` | For structures, the values are named, but that is irrelevant for the encoding (names are ignored - only order matters). All containers store elements consecutively. The order of the elements is not fixed, depends on the container, and cannot be relied on at decoding. This implicitly means that decoding some byte-array into a specified structure that enforces an order and then re-encoding it could result in a different byte array than the original that was decoded. | `{"votes": ["5GDyPHLVHcQYPTWfygtPYeo gQjyZy7J9fsi4brPhgEFq4pcv"], "id": 4}` | `0x04b80269ec500e458a630846b99105c397ee57 4125823d6f4388e9c7572e115c0504000000` | 44 | 45 | ## License 46 | https://github.com/polkascan/py-scale-codec/blob/master/LICENSE 47 | -------------------------------------------------------------------------------- /test/test_type_encoding.py: -------------------------------------------------------------------------------- 1 | # Python SCALE Codec Library 2 | # 3 | # Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | import os 17 | import unittest 18 | 19 | from scalecodec.base import ScaleBytes, ScaleDecoder, RuntimeConfiguration 20 | from scalecodec.type_registry import load_type_registry_preset, load_type_registry_file 21 | 22 | from scalecodec.types import CompactU32, Struct 23 | 24 | 25 | class TestScaleTypeEncoding(unittest.TestCase): 26 | 27 | @classmethod 28 | def setUpClass(cls): 29 | 30 | module_path = os.path.dirname(__file__) 31 | cls.metadata_fixture_dict = load_type_registry_file( 32 | os.path.join(module_path, 'fixtures', 'metadata_hex.json') 33 | ) 34 | 35 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("core")) 36 | 37 | cls.metadata_decoder = RuntimeConfiguration().create_scale_object( 38 | 'MetadataVersioned', data=ScaleBytes(cls.metadata_fixture_dict["kusama_test"]) 39 | ) 40 | 41 | cls.metadata_decoder.decode() 42 | 43 | def setUp(self) -> None: 44 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("kusama")) 45 | 46 | def tearDown(self) -> None: 47 | RuntimeConfiguration().clear_type_registry() 48 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("legacy")) 49 | 50 | def test_u16(self): 51 | obj = RuntimeConfiguration().create_scale_object('u16') 52 | obj.encode(64302) 53 | self.assertEqual(str(obj.data), "0x2efb") 54 | 55 | def test_i16(self): 56 | obj = RuntimeConfiguration().create_scale_object('i16') 57 | obj.encode(-1234) 58 | self.assertEqual(str(obj.data), "0x2efb") 59 | 60 | def test_i16_out_of_bounds(self): 61 | obj = RuntimeConfiguration().create_scale_object('i16') 62 | self.assertRaises(ValueError, obj.encode, -32769) 63 | 64 | def test_f64(self): 65 | obj = RuntimeConfiguration().create_scale_object('f64') 66 | obj.encode(-0.0) 67 | self.assertEqual(str(obj.data), "0x0000000000000080") 68 | 69 | def test_f64_invalid_input(self): 70 | obj = RuntimeConfiguration().create_scale_object('f64') 71 | with self.assertRaises(ValueError) as cm: 72 | obj.encode(-0) 73 | self.assertEqual('0 is not a float', str(cm.exception)) 74 | 75 | def test_f32(self): 76 | obj = RuntimeConfiguration().create_scale_object('f32') 77 | obj.encode(-0.0) 78 | self.assertEqual(str(obj.data), "0x00000080") 79 | 80 | def test_compact_u32_1byte(self): 81 | obj = RuntimeConfiguration().create_scale_object('Compact', ScaleBytes("0x18")) 82 | obj.decode() 83 | 84 | obj = RuntimeConfiguration().create_scale_object('Compact') 85 | obj.encode(6) 86 | self.assertEqual(str(obj.data), "0x18") 87 | 88 | def test_compact_u32_2bytes(self): 89 | obj = RuntimeConfiguration().create_scale_object('Compact', ScaleBytes("0x18")) 90 | obj.decode() 91 | 92 | obj = RuntimeConfiguration().create_scale_object('Compact') 93 | obj.encode(6000) 94 | self.assertEqual(str(obj.data), "0xc15d") 95 | 96 | def test_compact_u32_4bytes(self): 97 | 98 | obj = RuntimeConfiguration().create_scale_object('Compact') 99 | obj.encode(1000000) 100 | self.assertEqual(str(obj.data), "0x02093d00") 101 | 102 | def test_compact_u32_larger_than_4bytes(self): 103 | 104 | obj = RuntimeConfiguration().create_scale_object('Compact') 105 | obj.encode(150000000000000) 106 | self.assertEqual(str(obj.data), "0x0b0060b7986c88") 107 | 108 | def test_compact_u32_encode_decode(self): 109 | 110 | value = 2000001 111 | 112 | obj = RuntimeConfiguration().create_scale_object('Compact') 113 | data = obj.encode(value) 114 | 115 | obj = CompactU32(data) 116 | 117 | self.assertEqual(obj.decode(), value) 118 | 119 | def test_compact_u32_encode_decode_large(self): 120 | 121 | value = 2**30 122 | 123 | obj = CompactU32(ScaleBytes(bytearray())) 124 | data = obj.encode(value) 125 | 126 | obj = CompactU32(data) 127 | 128 | self.assertEqual(obj.decode(), value) 129 | 130 | def test_vec_string_encode_decode(self): 131 | 132 | value = ['test', 'vec'] 133 | 134 | obj = RuntimeConfiguration().create_scale_object('Vec') 135 | data = obj.encode(value) 136 | 137 | obj = RuntimeConfiguration().create_scale_object('Vec', data) 138 | 139 | self.assertEqual(obj.decode(), value) 140 | 141 | def test_vec_accountid_encode_decode(self): 142 | 143 | value = [ 144 | '0x0034d9d2dcdcd79451d95fd019a056d47dfa9926d762b94e63f06391b1545aee', 145 | '0x2ce1929ab903f695bdeeeb79a588774d71468362129136f1b7f7b31a32958f98', 146 | '0x88c47944e4aaf9d53a9627400f9a948bb5f355bda38702dbdeda0c5d34553128', 147 | ] 148 | 149 | obj = RuntimeConfiguration().create_scale_object('Vec') 150 | data = obj.encode(value) 151 | 152 | obj = RuntimeConfiguration().create_scale_object('Vec', data) 153 | 154 | self.assertEqual(obj.decode(), value) 155 | 156 | def test_bytes_encode_decode(self): 157 | 158 | value = 'This is a test' 159 | 160 | obj = RuntimeConfiguration().create_scale_object('Bytes') 161 | data = obj.encode(value) 162 | 163 | obj_check = RuntimeConfiguration().create_scale_object('Bytes', data) 164 | 165 | self.assertEqual(obj_check.decode(), value) 166 | 167 | def test_bytes_encode_bytes(self): 168 | value = b'This is a test' 169 | 170 | obj = RuntimeConfiguration().create_scale_object('Bytes') 171 | data = obj.encode(value) 172 | 173 | self.assertEqual("0x385468697320697320612074657374", data.to_hex()) 174 | 175 | def test_bytes_encode_bytearray(self): 176 | value = bytearray(b'This is a test') 177 | 178 | obj = RuntimeConfiguration().create_scale_object('Bytes') 179 | data = obj.encode(value) 180 | 181 | self.assertEqual("0x385468697320697320612074657374", data.to_hex()) 182 | 183 | def test_bytes_encode_list_of_u8(self): 184 | value = [84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 115, 116] 185 | 186 | obj = RuntimeConfiguration().create_scale_object('Bytes') 187 | data = obj.encode(value) 188 | 189 | self.assertEqual("0x385468697320697320612074657374", data.to_hex()) 190 | 191 | def test_hexbytes_encode_decode(self): 192 | 193 | value = '0x5468697320697320612074657374' 194 | 195 | obj = RuntimeConfiguration().create_scale_object('HexBytes') 196 | data = obj.encode(value) 197 | 198 | obj_check = RuntimeConfiguration().create_scale_object('HexBytes', data) 199 | 200 | self.assertEqual(obj_check.decode(), value) 201 | 202 | def test_accountid_encode_decode(self): 203 | value = '0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409' 204 | 205 | obj = RuntimeConfiguration().create_scale_object('AccountId') 206 | data = obj.encode(value) 207 | 208 | obj_check = RuntimeConfiguration().create_scale_object('AccountId', data) 209 | 210 | self.assertEqual(obj_check.decode(), value) 211 | 212 | def test_compact_balance_encode_decode(self): 213 | scale_data = ScaleBytes('0x070010a5d4e8') 214 | value = 1000000000000 215 | 216 | obj = RuntimeConfiguration().create_scale_object('Compact') 217 | data = obj.encode(value) 218 | 219 | self.assertEqual(str(scale_data), str(data)) 220 | 221 | obj_check = RuntimeConfiguration().create_scale_object('Compact', data) 222 | 223 | self.assertEqual(obj_check.decode(), value) 224 | 225 | def test_struct_encode_decode(self): 226 | 227 | value = {'unstake_threshold': 3, 'validator_payment': 0} 228 | scale_data = ScaleBytes("0x0c00") 229 | 230 | obj = RuntimeConfiguration().create_scale_object('ValidatorPrefsTo145') 231 | data = obj.encode(value) 232 | 233 | self.assertEqual(str(scale_data), str(data)) 234 | 235 | obj_check = RuntimeConfiguration().create_scale_object('ValidatorPrefsTo145', data) 236 | 237 | self.assertEqual(obj_check.decode(), value) 238 | 239 | def test_struct_encode_tuple(self): 240 | 241 | TestStruct = type('TestStruct', (Struct,), { 242 | 'type_mapping': (('aye', 'u32'), ('nay', 'u32')) 243 | }) 244 | 245 | obj = TestStruct() 246 | data = obj.encode((4, 2)) 247 | 248 | self.assertEqual(ScaleBytes("0x0400000002000000"), data) 249 | 250 | def test_struct_encode_int(self): 251 | 252 | TestStruct = type('TestStruct', (Struct,), { 253 | 'type_mapping': (('nonce', 'u32'),) 254 | }) 255 | 256 | obj = TestStruct() 257 | data = obj.encode(1) 258 | 259 | self.assertEqual(ScaleBytes("0x01000000"), data) 260 | 261 | # def test_struct_raw_encode(self): 262 | # RuntimeConfiguration().update_type_registry_types({ 263 | # "TestKeys": { 264 | # "type": "struct", 265 | # "type_mapping": [ 266 | # ["grandpa", "AccountId"], 267 | # ["babe", "AccountId"], 268 | # ["im_online", "AccountId"], 269 | # ["authority_discovery", "AccountId"], 270 | # ["parachains", "AccountId"] 271 | # ] 272 | # }, 273 | # }) 274 | # 275 | # value = {'unstakeThreshold': 3, 'validatorPayment': 0} 276 | # scale_data = ScaleBytes("0x0c00") 277 | # 278 | # obj = RuntimeConfiguration().create_scale_object('TestKeys') 279 | # data = obj.encode("0x824501a379ab300390fe6d8bfa19c52bf01ec5e5dad515d5bdb10dbe421dd1b318f8dfa2c79e2d691043939acf37596e84e35b2b9ddc34d849c3e31c5b5b290380827a5de7e1f0e4fea3d3bdf4f8191d7eadf2d78c802c95ca61e7c08b6415453207d5d24f35e6ea03240a7e7f5fcb98787cbed77d743d40aad2900be1760a6a") 280 | # self.assertEqual(data.to_hex(), "0x824501a379ab300390fe6d8bfa19c52bf01ec5e5dad515d5bdb10dbe421dd1b318f8dfa2c79e2d691043939acf37596e84e35b2b9ddc34d849c3e31c5b5b290380827a5de7e1f0e4fea3d3bdf4f8191d7eadf2d78c802c95ca61e7c08b6415453207d5d24f35e6ea03240a7e7f5fcb98787cbed77d743d40aad2900be1760a6a") 281 | 282 | def test_enum_encode_decode(self): 283 | 284 | value = {'Staked': None} 285 | 286 | obj = RuntimeConfiguration().create_scale_object('RewardDestination') 287 | data = obj.encode(value) 288 | 289 | obj_check = RuntimeConfiguration().create_scale_object('RewardDestination', data) 290 | 291 | self.assertEqual(obj_check.decode(), 'Staked') 292 | 293 | def test_enum_type_mapping_encode_decode(self): 294 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("test")) 295 | 296 | value = {"AuthoritiesChange": ["0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409"]} 297 | 298 | obj = RuntimeConfiguration().create_scale_object('DigestItem') 299 | data = obj.encode(value) 300 | 301 | obj_check = RuntimeConfiguration().create_scale_object('DigestItem', data) 302 | 303 | self.assertEqual(obj_check.decode(), value) 304 | 305 | def test_enum_type_mapping_empty_value_encode_decode(self): 306 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("test")) 307 | 308 | value = "Error" 309 | 310 | obj = RuntimeConfiguration().create_scale_object('EnumWithoutBaseClass') 311 | data = obj.encode(value) 312 | 313 | obj_check = RuntimeConfiguration().create_scale_object('EnumWithoutBaseClass', data) 314 | 315 | self.assertEqual(obj_check.decode(), value) 316 | 317 | def test_option_empty_encode_decode(self): 318 | 319 | value = None 320 | 321 | obj = RuntimeConfiguration().create_scale_object('Option') 322 | data = obj.encode(value) 323 | 324 | obj_check = RuntimeConfiguration().create_scale_object('Option', data) 325 | 326 | self.assertEqual(obj_check.decode(), value) 327 | 328 | def test_option_bytes_encode_decode(self): 329 | value = "Test" 330 | 331 | obj = RuntimeConfiguration().create_scale_object('Option') 332 | data = obj.encode(value) 333 | 334 | obj_check = RuntimeConfiguration().create_scale_object('Option', data) 335 | 336 | self.assertEqual(obj_check.decode(), value) 337 | 338 | def test_proposal_encode_decode(self): 339 | 340 | value = { 341 | 'call_module': 'System', 342 | 'call_function': 'remark', 343 | 'call_args': { 344 | '_remark': '0x0123456789' 345 | } 346 | } 347 | 348 | obj = RuntimeConfiguration().create_scale_object('Box', metadata=self.metadata_decoder) 349 | data = obj.encode(value) 350 | 351 | obj_check = RuntimeConfiguration().create_scale_object('Box', data, metadata=self.metadata_decoder) 352 | 353 | obj_check.decode() 354 | 355 | self.assertEqual(obj_check.value['call_module'], 'System') 356 | self.assertEqual(obj_check.value['call_function'], 'remark') 357 | self.assertEqual(obj_check.value['call_args'][0]['value'], '0x0123456789') 358 | 359 | def test_set_encode_decode(self): 360 | 361 | RuntimeConfiguration().update_type_registry(load_type_registry_preset("test")) 362 | 363 | value = ['Display', 'Legal', 'Email', 'Twitter'] 364 | 365 | obj = RuntimeConfiguration().create_scale_object('IdentityFields') 366 | scale_data = obj.encode(value) 367 | 368 | obj = RuntimeConfiguration().create_scale_object('IdentityFields', scale_data) 369 | obj.decode() 370 | 371 | self.assertEqual(obj.value, value) 372 | 373 | def test_data_encode_decode(self): 374 | 375 | value = {"Raw": "Test"} 376 | 377 | obj = RuntimeConfiguration().create_scale_object('Data') 378 | scale_data = obj.encode(value) 379 | 380 | obj = RuntimeConfiguration().create_scale_object('Data', scale_data) 381 | obj.decode() 382 | 383 | self.assertEqual(obj.value, value) 384 | 385 | def test_multi_encode(self): 386 | 387 | as_multi = RuntimeConfiguration().create_scale_object("Call", metadata=self.metadata_decoder) 388 | 389 | as_multi.encode( 390 | { 391 | "call_module": "Multisig", 392 | "call_function": "as_multi", 393 | "call_args": { 394 | "call": { 395 | "call_module": "Balances", 396 | "call_function": "transfer", 397 | "call_args": { 398 | "dest": "CofvaLbP3m8PLeNRQmLVPWmTT7jGgAXTwyT69k2wkfPxJ9V", 399 | "value": 10000000000000 400 | }, 401 | }, 402 | "maybe_timepoint": {"height": 3012294, "index": 3}, 403 | "other_signatories": sorted(['D2bHQwFcQj11SvtkjULEdKhK4WAeP6MThXgosMHjW9DrmbE', 404 | 'CofvaLbP3m8PLeNRQmLVPWmTT7jGgAXTwyT69k2wkfPxJ9V']), 405 | "threshold": 2, 406 | "store_call": True, 407 | "max_weight": 10, 408 | }, 409 | } 410 | ) 411 | self.assertEqual(str(as_multi.data), "0x1f010200080a2ee2acc37fa96e818e2817afc104ce55770bcccb7333bbf8481d5bc3c6fa4614097421065c7bb0efc6770ffc5d604654159d45910cc7a3cb602be16acc552801c6f62d0003000000a80400000a2ee2acc37fa96e818e2817afc104ce55770bcccb7333bbf8481d5bc3c6fa460b00a0724e1809010a00000000000000") 412 | 413 | def test_call_encode_invalid_type(self): 414 | call = RuntimeConfiguration().create_scale_object("Call", metadata=self.metadata_decoder) 415 | self.assertRaises(TypeError, call.encode, '{"call_module": "Balances", "call_function": "transfer"}') 416 | self.assertRaises(TypeError, call.encode, 2) 417 | 418 | def test_era_immortal_encode(self): 419 | obj = RuntimeConfiguration().create_scale_object('Era') 420 | obj.encode('00') 421 | self.assertEqual(str(obj.data), '0x00') 422 | 423 | def test_era_mortal_encode(self): 424 | obj = RuntimeConfiguration().create_scale_object('Era') 425 | obj.encode((32768, 20000)) 426 | self.assertEqual(str(obj.data), '0x4e9c') 427 | 428 | obj = RuntimeConfiguration().create_scale_object('Era') 429 | obj.encode((64, 60)) 430 | self.assertEqual(str(obj.data), '0xc503') 431 | 432 | obj = RuntimeConfiguration().create_scale_object('Era') 433 | obj.encode((64, 40)) 434 | self.assertEqual(str(obj.data), '0x8502') 435 | 436 | def test_era_mortal_encode_dict(self): 437 | obj = RuntimeConfiguration().create_scale_object('Era') 438 | obj.encode({'period': 32768, 'phase': 20000}) 439 | self.assertEqual(str(obj.data), '0x4e9c') 440 | 441 | obj = RuntimeConfiguration().create_scale_object('Era') 442 | obj.encode({'period': 32768, 'current': (32768 * 3) + 20000}) 443 | self.assertEqual(str(obj.data), '0x4e9c') 444 | 445 | obj = RuntimeConfiguration().create_scale_object('Era') 446 | obj.encode({'period': 200, 'current': 1400}) 447 | obj2 = RuntimeConfiguration().create_scale_object('Era') 448 | obj2.encode((256, 120)) 449 | self.assertEqual(str(obj.data), str(obj2.data)) 450 | 451 | def test_encode_accept_derived_class(self): 452 | 453 | RuntimeConfiguration().update_type_registry_types({"DerivedCall": "Call"}) 454 | 455 | call = RuntimeConfiguration().create_scale_object('Call', metadata=self.metadata_decoder) 456 | 457 | call.encode({ 458 | 'call_module': 'Balances', 459 | 'call_function': 'transfer', 460 | 'call_args': { 461 | 'dest': 'EaG2CRhJWPb7qmdcJvy3LiWdh26Jreu9Dx6R1rXxPmYXoDk', 462 | 'value': 1000000000000 463 | } 464 | }) 465 | DerivedCall = type("DerivedCall", (call.__class__,), {}) 466 | derived_call = DerivedCall(data=None, metadata=self.metadata_decoder) 467 | 468 | derived_call.encode(call) 469 | 470 | self.assertEqual(call.data, derived_call.data) 471 | self.assertEqual(call.value_object, derived_call.value_object) 472 | self.assertEqual(call.value_serialized, derived_call.value_serialized) 473 | 474 | # def test_all_subclasses_implement_encode(self): 475 | # for scale_type_cls in RuntimeConfiguration.all_subclasses(ScaleType): 476 | # try: 477 | # obj = scale_type_cls() 478 | # except TypeError as e: 479 | # pass 480 | # 481 | # try: 482 | # obj.process_encode(None) 483 | # except NotImplementedError: 484 | # self.fail(f'{scale_type_cls.__name__} didn\'t implement process_encode') 485 | # except Exception as e: 486 | # pass 487 | 488 | -------------------------------------------------------------------------------- /scalecodec/type_registry/karura.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": { 3 | "CallOf": "Call", 4 | "DispatchTime": { 5 | "type": "enum", 6 | "type_mapping": [ 7 | [ 8 | "At", 9 | "BlockNumber" 10 | ], 11 | [ 12 | "After", 13 | "BlockNumber" 14 | ] 15 | ] 16 | }, 17 | "ScheduleTaskIndex": "u32", 18 | "DelayedOrigin": { 19 | "type": "struct", 20 | "type_mapping": [ 21 | [ 22 | "delay", 23 | "BlockNumber" 24 | ], 25 | [ 26 | "origin", 27 | "PalletsOrigin" 28 | ] 29 | ] 30 | }, 31 | "AuthorityOrigin": "DelayedOrigin", 32 | "StorageValue": "Vec", 33 | "GraduallyUpdate": { 34 | "type": "struct", 35 | "type_mapping": [ 36 | [ 37 | "key", 38 | "StorageKey" 39 | ], 40 | [ 41 | "targetValue", 42 | "StorageValue" 43 | ], 44 | [ 45 | "perBlock", 46 | "StorageValue" 47 | ] 48 | ] 49 | }, 50 | "StorageKeyBytes": "Vec", 51 | "StorageValueBytes": "Vec", 52 | "RpcDataProviderId": "Text", 53 | "DataProviderId": "u8", 54 | "TimestampedValue": { 55 | "type": "struct", 56 | "type_mapping": [ 57 | [ 58 | "value", 59 | "OracleValue" 60 | ], 61 | [ 62 | "timestamp", 63 | "Moment" 64 | ] 65 | ] 66 | }, 67 | "TimestampedValueOf": "TimestampedValue", 68 | "OrderedSet": "Vec", 69 | "OrmlAccountData": { 70 | "type": "struct", 71 | "type_mapping": [ 72 | [ 73 | "free", 74 | "Balance" 75 | ], 76 | [ 77 | "frozen", 78 | "Balance" 79 | ], 80 | [ 81 | "reserved", 82 | "Balance" 83 | ] 84 | ] 85 | }, 86 | "OrmlBalanceLock": { 87 | "type": "struct", 88 | "type_mapping": [ 89 | [ 90 | "amount", 91 | "Balance" 92 | ], 93 | [ 94 | "id", 95 | "LockIdentifier" 96 | ] 97 | ] 98 | }, 99 | "AuctionInfo": { 100 | "type": "struct", 101 | "type_mapping": [ 102 | [ 103 | "bid", 104 | "Option<(AccountId, Balance)>" 105 | ], 106 | [ 107 | "start", 108 | "BlockNumber" 109 | ], 110 | [ 111 | "end", 112 | "Option" 113 | ] 114 | ] 115 | }, 116 | "DelayedDispatchTime": { 117 | "type": "enum", 118 | "type_mapping": [ 119 | [ 120 | "At", 121 | "BlockNumber" 122 | ], 123 | [ 124 | "After", 125 | "BlockNumber" 126 | ] 127 | ] 128 | }, 129 | "DispatchId": "u32", 130 | "Price": "FixedU128", 131 | "OrmlVestingSchedule": { 132 | "type": "struct", 133 | "type_mapping": [ 134 | [ 135 | "start", 136 | "BlockNumber" 137 | ], 138 | [ 139 | "period", 140 | "BlockNumber" 141 | ], 142 | [ 143 | "periodCount", 144 | "u32" 145 | ], 146 | [ 147 | "perPeriod", 148 | "Compact" 149 | ] 150 | ] 151 | }, 152 | "VestingScheduleOf": "OrmlVestingSchedule", 153 | "PoolInfo": { 154 | "type": "struct", 155 | "type_mapping": [ 156 | [ 157 | "totalShares", 158 | "Compact" 159 | ], 160 | [ 161 | "totalRewards", 162 | "Compact" 163 | ], 164 | [ 165 | "totalWithdrawnRewards", 166 | "Compact" 167 | ] 168 | ] 169 | }, 170 | "Share": "u128", 171 | "OracleValue": "Price", 172 | "PalletBalanceOf": "Balance", 173 | "CollateralAuctionItem": { 174 | "type": "struct", 175 | "type_mapping": [ 176 | [ 177 | "refundRecipient", 178 | "AccountId" 179 | ], 180 | [ 181 | "currencyId", 182 | "CurrencyId" 183 | ], 184 | [ 185 | "initialAmount", 186 | "Compact" 187 | ], 188 | [ 189 | "amount", 190 | "Compact" 191 | ], 192 | [ 193 | "target", 194 | "Compact" 195 | ], 196 | [ 197 | "startTime", 198 | "BlockNumber" 199 | ] 200 | ] 201 | }, 202 | "DebitAuctionItem": { 203 | "type": "struct", 204 | "type_mapping": [ 205 | [ 206 | "initialAmount", 207 | "Compact" 208 | ], 209 | [ 210 | "amount", 211 | "Compact" 212 | ], 213 | [ 214 | "fix", 215 | "Compact" 216 | ], 217 | [ 218 | "startTime", 219 | "BlockNumber" 220 | ] 221 | ] 222 | }, 223 | "SurplusAuctionItem": { 224 | "type": "struct", 225 | "type_mapping": [ 226 | [ 227 | "amount", 228 | "Compact" 229 | ], 230 | [ 231 | "startTime", 232 | "BlockNumber" 233 | ] 234 | ] 235 | }, 236 | "Exchange": { 237 | "type": "enum", 238 | "value_list": [ 239 | "Auction", 240 | "Exchange" 241 | ] 242 | }, 243 | "OptionRate": "Option", 244 | "OptionRatio": "Option", 245 | "ChangeOptionRate": { 246 | "type": "enum", 247 | "type_mapping": [ 248 | [ 249 | "NoChange", 250 | "Null" 251 | ], 252 | [ 253 | "NewValue", 254 | "OptionRate" 255 | ] 256 | ] 257 | }, 258 | "ChangeOptionRatio": { 259 | "type": "enum", 260 | "type_mapping": [ 261 | [ 262 | "NoChange", 263 | "Null" 264 | ], 265 | [ 266 | "NewValue", 267 | "OptionRatio" 268 | ] 269 | ] 270 | }, 271 | "ChangeBalance": { 272 | "type": "enum", 273 | "type_mapping": [ 274 | [ 275 | "NoChange", 276 | "Null" 277 | ], 278 | [ 279 | "NewValue", 280 | "Balance" 281 | ] 282 | ] 283 | }, 284 | "RiskManagementParams": { 285 | "type": "struct", 286 | "type_mapping": [ 287 | [ 288 | "maximumTotalDebitValue", 289 | "Balance" 290 | ], 291 | [ 292 | "interestRatePerSec", 293 | "Option" 294 | ], 295 | [ 296 | "liquidationRatio", 297 | "Option" 298 | ], 299 | [ 300 | "liquidationPenalty", 301 | "Option" 302 | ], 303 | [ 304 | "requiredCollateralRatio", 305 | "Option" 306 | ] 307 | ] 308 | }, 309 | "CandidateInfoOf": "CandidateInfo", 310 | "TradingPairProvisionParameters": { 311 | "type": "struct", 312 | "type_mapping": [ 313 | [ 314 | "minContribution", 315 | "(Balance, Balance)" 316 | ], 317 | [ 318 | "targetProvision", 319 | "(Balance, Balance)" 320 | ], 321 | [ 322 | "accumulatedProvision", 323 | "(Balance, Balance)" 324 | ], 325 | [ 326 | "notBefore", 327 | "BlockNumber" 328 | ] 329 | ] 330 | }, 331 | "BalanceWrapper": { 332 | "type": "struct", 333 | "type_mapping": [ 334 | [ 335 | "amount", 336 | "Balance" 337 | ] 338 | ] 339 | }, 340 | "BalanceRequest": { 341 | "type": "struct", 342 | "type_mapping": [ 343 | [ 344 | "amount", 345 | "Balance" 346 | ] 347 | ] 348 | }, 349 | "TradingPairStatus": { 350 | "type": "enum", 351 | "type_mapping": [ 352 | [ 353 | "NotEnabled", 354 | "Null" 355 | ], 356 | [ 357 | "Provisioning", 358 | "TradingPairProvisionParameters" 359 | ], 360 | [ 361 | "Enabled", 362 | "Null" 363 | ] 364 | ] 365 | }, 366 | "Erc20Info": { 367 | "type": "struct", 368 | "type_mapping": [ 369 | [ 370 | "address", 371 | "EvmAddress" 372 | ], 373 | [ 374 | "name", 375 | "Vec" 376 | ], 377 | [ 378 | "symbol", 379 | "Vec" 380 | ], 381 | [ 382 | "decimals", 383 | "u8" 384 | ] 385 | ] 386 | }, 387 | "EstimateResourcesResponse": { 388 | "type": "struct", 389 | "type_mapping": [ 390 | [ 391 | "gas", 392 | "u256" 393 | ], 394 | [ 395 | "storage", 396 | "i32" 397 | ], 398 | [ 399 | "weightFee", 400 | "u256" 401 | ] 402 | ] 403 | }, 404 | "EvmAccountInfo": { 405 | "type": "struct", 406 | "type_mapping": [ 407 | [ 408 | "nonce", 409 | "Index" 410 | ], 411 | [ 412 | "contractInfo", 413 | "Option" 414 | ] 415 | ] 416 | }, 417 | "CodeInfo": { 418 | "type": "struct", 419 | "type_mapping": [ 420 | [ 421 | "codeSize", 422 | "u32" 423 | ], 424 | [ 425 | "refCount", 426 | "u32" 427 | ] 428 | ] 429 | }, 430 | "EvmContractInfo": { 431 | "type": "struct", 432 | "type_mapping": [ 433 | [ 434 | "codeHash", 435 | "H256" 436 | ], 437 | [ 438 | "maintainer", 439 | "H160" 440 | ], 441 | [ 442 | "deployed", 443 | "bool" 444 | ] 445 | ] 446 | }, 447 | "EvmAddress": "H160", 448 | "CallRequest": { 449 | "type": "struct", 450 | "type_mapping": [ 451 | [ 452 | "from", 453 | "Option" 454 | ], 455 | [ 456 | "to", 457 | "Option" 458 | ], 459 | [ 460 | "gasLimit", 461 | "Option" 462 | ], 463 | [ 464 | "storageLimit", 465 | "Option" 466 | ], 467 | [ 468 | "value", 469 | "Option" 470 | ], 471 | [ 472 | "data", 473 | "Option" 474 | ] 475 | ] 476 | }, 477 | "RedeemStrategy": { 478 | "type": "enum", 479 | "type_mapping": [ 480 | [ 481 | "Immediately", 482 | "Null" 483 | ], 484 | [ 485 | "Target", 486 | "EraIndex" 487 | ], 488 | [ 489 | "WaitForUnbonding", 490 | "Null" 491 | ] 492 | ] 493 | }, 494 | "RelaychainAccountId": "AccountId", 495 | "SlashInfo": { 496 | "type": "struct", 497 | "type_mapping": [ 498 | [ 499 | "validator", 500 | "RelaychainAccountId" 501 | ], 502 | [ 503 | "relaychainTokenAmount", 504 | "Balance" 505 | ] 506 | ] 507 | }, 508 | "ValidatorBacking": { 509 | "type": "struct", 510 | "type_mapping": [ 511 | [ 512 | "totalInsurance", 513 | "Balance" 514 | ], 515 | [ 516 | "isFrozen", 517 | "bool" 518 | ] 519 | ] 520 | }, 521 | "Guarantee": { 522 | "type": "struct", 523 | "type_mapping": [ 524 | [ 525 | "total", 526 | "Balance" 527 | ], 528 | [ 529 | "bonded", 530 | "Balance" 531 | ], 532 | [ 533 | "unbonding", 534 | "Option<(Balance, BlockNumber)>" 535 | ] 536 | ] 537 | }, 538 | "PoolId": { 539 | "type": "enum", 540 | "type_mapping": [ 541 | [ 542 | "LoansIncentive", 543 | "CurrencyId" 544 | ], 545 | [ 546 | "DexIncentive", 547 | "CurrencyId" 548 | ], 549 | [ 550 | "HomaIncentive", 551 | "Null" 552 | ], 553 | [ 554 | "DexSaving", 555 | "CurrencyId" 556 | ], 557 | [ 558 | "HomaValidatorAllowance", 559 | "AccountId" 560 | ] 561 | ] 562 | }, 563 | "Position": { 564 | "type": "struct", 565 | "type_mapping": [ 566 | [ 567 | "collateral", 568 | "Balance" 569 | ], 570 | [ 571 | "debit", 572 | "Balance" 573 | ] 574 | ] 575 | }, 576 | "CID": "Vec", 577 | "Attributes": "BTreeMap, Vec>", 578 | "TokenInfoOf": { 579 | "type": "struct", 580 | "type_mapping": [ 581 | [ 582 | "metadata", 583 | "CID" 584 | ], 585 | [ 586 | "owner", 587 | "AccountId" 588 | ], 589 | [ 590 | "data", 591 | "TokenData" 592 | ] 593 | ] 594 | }, 595 | "Properties": { 596 | "type": "struct", 597 | "type_mapping": [ 598 | [ 599 | "_set", 600 | { 601 | "_bitLength": 8, 602 | "Transferable": 1, 603 | "Burnable": 2, 604 | "Mintable": 4, 605 | "ClassPropertiesMutable": 8 606 | } 607 | ] 608 | ] 609 | }, 610 | "ClassData": { 611 | "type": "struct", 612 | "type_mapping": [ 613 | [ 614 | "deposit", 615 | "Balance" 616 | ], 617 | [ 618 | "properties", 619 | "Properties" 620 | ], 621 | [ 622 | "attributes", 623 | "Attributes" 624 | ] 625 | ] 626 | }, 627 | "TokenData": { 628 | "type": "struct", 629 | "type_mapping": [ 630 | [ 631 | "deposit", 632 | "Balance" 633 | ], 634 | [ 635 | "attributes", 636 | "Attributes" 637 | ] 638 | ] 639 | }, 640 | "TokenId": "u64", 641 | "TokenIdOf": "TokenId", 642 | "NFTClassId": "u32", 643 | "ClassIdOf": "ClassId", 644 | "NFTBalance": "u128", 645 | "NFTBalanceOf": "NFTBalance", 646 | "ClassInfoOf": { 647 | "type": "struct", 648 | "type_mapping": [ 649 | [ 650 | "metadata", 651 | "CID" 652 | ], 653 | [ 654 | "totalIssuance", 655 | "TokenId" 656 | ], 657 | [ 658 | "owner", 659 | "AccountId" 660 | ], 661 | [ 662 | "data", 663 | "ClassData" 664 | ] 665 | ] 666 | }, 667 | "NomineeId": "AccountId", 668 | "HomaUnlockChunk": { 669 | "type": "struct", 670 | "type_mapping": [ 671 | [ 672 | "value", 673 | "Balance" 674 | ], 675 | [ 676 | "era", 677 | "EraIndex" 678 | ] 679 | ] 680 | }, 681 | "BondingLedger": { 682 | "type": "struct", 683 | "type_mapping": [ 684 | [ 685 | "total", 686 | "Balance" 687 | ], 688 | [ 689 | "active", 690 | "Balance" 691 | ], 692 | [ 693 | "unlocking", 694 | "Vec" 695 | ] 696 | ] 697 | }, 698 | "Amount": "i128", 699 | "AmountOf": "Amount", 700 | "AuctionId": "u32", 701 | "AuctionIdOf": "AuctionId", 702 | "TokenSymbol": { 703 | "type": "enum", 704 | "value_list": { 705 | "ACA": 0, 706 | "AUSD": 1, 707 | "DOT": 2, 708 | "LDOT": 3, 709 | "RENBTC": 4, 710 | "KAR": 128, 711 | "KUSD": 129, 712 | "KSM": 130, 713 | "LKSM": 131, 714 | "CASH": 140 715 | } 716 | }, 717 | "DexShare": { 718 | "type": "enum", 719 | "type_mapping": [ 720 | [ 721 | "Token", 722 | "TokenSymbol" 723 | ], 724 | [ 725 | "Erc20", 726 | "EvmAddress" 727 | ] 728 | ] 729 | }, 730 | "CurrencyId": { 731 | "type": "enum", 732 | "type_mapping": [ 733 | [ 734 | "Token", 735 | "TokenSymbol" 736 | ], 737 | [ 738 | "DEXShare", 739 | "(DexShare, DexShare)" 740 | ], 741 | [ 742 | "ERC20", 743 | "EvmAddress" 744 | ], 745 | [ 746 | "ChainSafe", 747 | "[u8; 32]" 748 | ] 749 | ] 750 | }, 751 | "CurrencyIdOf": "CurrencyId", 752 | "ACA": { 753 | "type": "enum", 754 | "value_list": [ 755 | "KAR", 756 | "ACA" 757 | ] 758 | }, 759 | "TreasuryReserve": { 760 | "type": "enum", 761 | "value_list": [ 762 | "Root", 763 | "Treasury", 764 | "HonzonTreasury", 765 | "HomaTreasury", 766 | "TreasuryReserve" 767 | ] 768 | }, 769 | "Band": { 770 | "type": "enum", 771 | "value_list": [ 772 | "Aggregated", 773 | "Acala", 774 | "Band" 775 | ] 776 | }, 777 | "TradingPair": "(CurrencyId, CurrencyId)", 778 | "OracleKey": "CurrencyId", 779 | "AsOriginId": "AuthoritysOriginId", 780 | "Loan": { 781 | "type": "enum", 782 | "value_list": [ 783 | "Any", 784 | "CancelProxy", 785 | "Governance", 786 | "Auction", 787 | "Swap", 788 | "Loan" 789 | ] 790 | }, 791 | "SubAccountStatus": { 792 | "type": "struct", 793 | "type_mapping": [ 794 | [ 795 | "bonded", 796 | "Balance" 797 | ], 798 | [ 799 | "available", 800 | "Balance" 801 | ], 802 | [ 803 | "unbonding", 804 | "Vec<(EraIndex,Balance)>" 805 | ], 806 | [ 807 | "mockRewardRate", 808 | "Rate" 809 | ] 810 | ] 811 | }, 812 | "Params": { 813 | "type": "struct", 814 | "type_mapping": [ 815 | [ 816 | "targetMaxFreeUnbondedRatio", 817 | "Ratio" 818 | ], 819 | [ 820 | "targetMinFreeUnbondedRatio", 821 | "Ratio" 822 | ], 823 | [ 824 | "targetUnbondingToFreeRatio", 825 | "Ratio" 826 | ], 827 | [ 828 | "unbondingToFreeAdjustment", 829 | "Ratio" 830 | ], 831 | [ 832 | "baseFeeRate", 833 | "Rate" 834 | ] 835 | ] 836 | }, 837 | "Finished": { 838 | "type": "enum", 839 | "value_list": [ 840 | "Started", 841 | "RelaychainUpdated", 842 | "LedgerUpdated", 843 | "Finished" 844 | ] 845 | }, 846 | "Ledger": { 847 | "type": "struct", 848 | "type_mapping": [ 849 | [ 850 | "bonded", 851 | "Balance" 852 | ], 853 | [ 854 | "unbondingToFree", 855 | "Balance" 856 | ], 857 | [ 858 | "freePool", 859 | "Balance" 860 | ], 861 | [ 862 | "toUnbondNextEra", 863 | "(Balance, Balance)" 864 | ] 865 | ] 866 | }, 867 | "ChangeRate": { 868 | "type": "enum", 869 | "type_mapping": [ 870 | [ 871 | "NoChange", 872 | "Null" 873 | ], 874 | [ 875 | "NewValue", 876 | "Rate" 877 | ] 878 | ] 879 | }, 880 | "ChangeRatio": { 881 | "type": "enum", 882 | "type_mapping": [ 883 | [ 884 | "NoChange", 885 | "Null" 886 | ], 887 | [ 888 | "NewValue", 889 | "Ratio" 890 | ] 891 | ] 892 | }, 893 | "BalanceInfo": { 894 | "type": "struct", 895 | "type_mapping": [ 896 | [ 897 | "amount", 898 | "Balance" 899 | ] 900 | ] 901 | }, 902 | "PolkadotAccountId": "AccountId", 903 | "PolkadotAccountIdOf": "PolkadotAccountId", 904 | "ExchangeRate": "FixedU128", 905 | "Rate": "FixedU128", 906 | "Ratio": "FixedU128", 907 | "PublicKey": "[u8; 20]", 908 | "DestAddress": "Vec", 909 | "DepositNonce": "u64", 910 | "ResourceId": "[u8; 32]", 911 | "ChainId": "u8", 912 | "Keys": "SessionKeys1", 913 | "runtime_common::check_nonce::CheckNonce": "Compact" 914 | } 915 | } 916 | --------------------------------------------------------------------------------