Skip to main content

Library for interfacing with a Substrate node

Project description

Python Substrate Interface

Travis CI Build Status Latest Version Supported Python versions License

Python Substrate Interface Library

Description

This library specializes in interfacing with a Substrate node, providing additional convenience methods to deal with SCALE encoding/decoding (the default output and input format of the Substrate JSONRPC), metadata parsing, type registry management and versioning of types.

Documentation

https://polkascan.github.io/py-substrate-interface/

Installation

pip install substrate-interface

Examples

Initialization

The following examples show how to initialize for supported chains:

Polkadot

substrate = SubstrateInterface(
    url="wss://rpc.polkadot.io",
    address_type=0,
    type_registry_preset='polkadot'
)

Note on support for wss, this is still quite limited at the moment as connections are not reused yet. Until support is improved it is prefered to use http endpoints (e.g. http://127.0.0.1:9933)

Kusama

substrate = SubstrateInterface(
    url="wss://kusama-rpc.polkadot.io/",
    address_type=2,
    type_registry_preset='kusama'
)

Kulupu

substrate = SubstrateInterface(
    url="wss://rpc.kulupu.corepaper.org/ws",
    address_type=16,
    type_registry_preset='kulupu'
)

Substrate Node Template

Compatible with https://github.com/substrate-developer-hub/substrate-node-template

substrate = SubstrateInterface(
    url="http://127.0.0.1:9933",
    address_type=42,
    type_registry_preset='substrate-node-template'
)

If custom types are introduced in the Substrate chain, the following example will add compatibility by creating a custom type registry JSON file and including this during initialization:

{
  "runtime_id": 2,
  "types": {
    "MyCustomInt": "u32",
    "MyStruct": {
      "type": "struct",
      "type_mapping": [
         ["account", "AccountId"],
         ["message", "Vec<u8>"]
      ]
    }
  },
  "versioning": [
  ]
}
custom_type_registry = load_type_registry_file("my-custom-types.json")

substrate = SubstrateInterface(
    url="http://127.0.0.1:9933",
    address_type=42,
    type_registry_preset='substrate-node-template',
    type_registry=custom_type_registry
)

Get extrinsics for a certain block

# Set block_hash to None for chaintip
block_hash = "0x588930468212316d8a75ede0bec0bc949451c164e2cea07ccfc425f497b077b7"

# Retrieve extrinsics in block
result = substrate.get_runtime_block(block_hash=block_hash)

for extrinsic in result['block']['extrinsics']:

    if 'account_id' in extrinsic:
        signed_by_address = ss58_encode(address=extrinsic['account_id'], address_type=2)
    else:
        signed_by_address = None

    print('\nModule: {}\nCall: {}\nSigned by: {}'.format(
        extrinsic['call_module'],
        extrinsic['call_function'],
        signed_by_address
    ))

    # Loop through params
    for param in extrinsic['params']:

        if param['type'] == 'Address':
            param['value'] = ss58_encode(address=param['value'], address_type=2)

        if param['type'] == 'Compact<Balance>':
            param['value'] = '{} DOT'.format(param['value'] / 10**12)

        print("Param '{}': {}".format(param['name'], param['value']))

Make a storage call

The modules and storage functions are provided in the metadata (see substrate.get_metadata_storage_functions()), parameters will be automatically converted to SCALE-bytes (also including decoding of SS58 addresses).

balance_info = substrate.get_runtime_state(
    module='System',
    storage_function='Account',
    params=['5E9oDs9PjpsBbxXxRE9uMaZZhnBAV38n2ouLB28oecBDdeQo']
).get('result')

if balance_info:
    print("\n\nCurrent free balance: {} KSM".format(
        balance_info.get('data').get('free', 0) / 10**12
    ))

Or get a historic balance at a certain block hash:

balance_info = substrate.get_runtime_state(
    module='System',
    storage_function='Account',
    params=['5E9oDs9PjpsBbxXxRE9uMaZZhnBAV38n2ouLB28oecBDdeQo'],
    block_hash=block_hash
).get('result')

if balance_info:
    print("\n\nFree balance @ {}: {} KSM".format(
        block_hash,
        balance_info.get('data').get('free', 0) / 10**12
    ))

Or get all the key pairs of a map:

# Get all the stash and controller bondings.
all_bonded_stash_ctrls = substrate.iterate_map(
    module='Staking',
    storage_function='Bonded',
    block_hash=block_hash
)

Create and send signed extrinsics

The following code snippet illustrates how to create a call, wrap it in an signed extrinsic and send it to the network:

from substrateinterface import SubstrateInterface, SubstrateRequestException, Keypair

substrate = SubstrateInterface(
    url="ws://127.0.0.1:9944",
    address_type=42,
    type_registry_preset='kusama'
)

keypair = Keypair.create_from_mnemonic('episode together nose spoon dose oil faculty zoo ankle evoke admit walnut')

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer',
    call_params={
        'dest': '5E9oDs9PjpsBbxXxRE9uMaZZhnBAV38n2ouLB28oecBDdeQo',
        'value': 1 * 10**12
    }
)

extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)

try:
    result = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)
    print("Extrinsic '{}' sent and included in block '{}'".format(result['extrinsic_hash'], result['block_hash']))

except SubstrateRequestException as e:
    print("Failed to send: {}".format(e))

Keypair creation and signing

mnemonic = Keypair.generate_mnemonic()
keypair = Keypair.create_from_mnemonic(mnemonic)
signature = keypair.sign("Test123")
if keypair.verify("Test123", signature):
    print('Verified')

Create keypair using Subkey wrapper (using local subkey binary)

sub_key = Subkey(subkey_path='/usr/local/bin/subkey')
subkey_result = sub_key.inspect(network='kusama', suri="appear fortune produce assist volcano deal shoulder foot engine harvest pupil agent//Alice")
keypair = Keypair.create_from_seed(subkey_result["secretSeed"], address_type=2)

Create keypair using Subkey wrapper (using Docker image parity/subkey:latest)

sub_key = Subkey(use_docker=True)
subkey_result = sub_key.inspect(network='kusama', suri="appear fortune produce assist volcano deal shoulder foot engine harvest pupil agent//Alice")
keypair = Keypair.create_from_seed(subkey_result["secretSeed"], address_type=2)

Metadata and type versioning

Py-substrate-interface makes it also possible to easily interprete changed types and historic runtimes. As an example we create an (not very useful) historic call of a module that has been removed later on: retrieval of historic metadata and apply the correct version of types in the type registry is all done automatically. Because parsing of metadata and type registry is quite heavy, the result will be cached per runtime id. In the future there could be support for caching backends like Redis to make this cache more persistent.

Create an unsigned extrinsic of a module that was removed by providing block hash:

payload = substrate.compose_call(
    call_module='Nicks',
    call_function='clear_name',
    call_params={},
    block_hash="0x918107632d7994d50f3661db3af353d2aa378f696e47a393bab573f63f7d6c3a"
)

License

https://github.com/polkascan/py-substrate-interface/blob/master/LICENSE

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

substrate-interface-0.9.21.tar.gz (115.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

substrate_interface-0.9.21-py3-none-any.whl (123.7 kB view details)

Uploaded Python 3

File details

Details for the file substrate-interface-0.9.21.tar.gz.

File metadata

  • Download URL: substrate-interface-0.9.21.tar.gz
  • Upload date:
  • Size: 115.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.50.1 CPython/3.8.6

File hashes

Hashes for substrate-interface-0.9.21.tar.gz
Algorithm Hash digest
SHA256 f32bcc535d9786236ced101852ea25dd96bd68b8205c6365fba405cf7aaa5980
MD5 e6a08e2b0bfdc01f28cb737a469e94cd
BLAKE2b-256 db4fa4b9cfdda41ee61d7b271d85e46efef468c8dbf869d12f7793b0bcadce27

See more details on using hashes here.

File details

Details for the file substrate_interface-0.9.21-py3-none-any.whl.

File metadata

  • Download URL: substrate_interface-0.9.21-py3-none-any.whl
  • Upload date:
  • Size: 123.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.50.1 CPython/3.8.6

File hashes

Hashes for substrate_interface-0.9.21-py3-none-any.whl
Algorithm Hash digest
SHA256 d96b118b72879f3642b682fdebad273ff868b65cce1f9bc0f1602e6da612d45b
MD5 ad558e78f86aff4f9ffd18b13b9a0dff
BLAKE2b-256 cf4c09f7f6441a6c6ed0d4e3151d1067125f93e4bd396a2c07c9d9e9dc72ed26

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page