substrate-interface


Namesubstrate-interface JSON
Version 0.13.8 PyPI version JSON
download
home_pagehttps://github.com/polkascan/py-substrate-interface
SummaryLibrary for interfacing with a Substrate node
upload_time2021-06-08 12:16:34
maintainer
docs_urlNone
authorStichting Polkascan (Polkascan Foundation)
requires_python>=3.6, <4
license
keywords interface polkascan polkadot substrate blockchain rpc kusama
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Python Substrate Interface

[![Build Status](https://img.shields.io/github/workflow/status/polkascan/py-substrate-interface/Run%20unit%20tests)](https://github.com/polkascan/py-substrate-interface/actions?query=workflow%3A%22Run+unit+tests%22)
[![Latest Version](https://img.shields.io/pypi/v/substrate-interface.svg)](https://pypi.org/project/substrate-interface/)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/substrate-interface.svg)](https://pypi.org/project/substrate-interface/)
[![License](https://img.shields.io/pypi/l/substrate-interface.svg)](https://github.com/polkascan/py-substrate-interface/blob/master/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.

## Table of Contents

* [Documentation](#documentation)
* [Installation](#installation)
* [Initialization](#hello-world--the-flipper)
  * [Autodiscover mode](#autodiscover-mode)
  * [Manually set required properties](#manually-set-required-properties)  
  * [Substrate Node Template](#substrate-node-template)
* [Features](#features)
  * [Get extrinsics for a certain block](#retrieve-extrinsics-for-a-certain-block)
  * [Subscribe to new block headers](#subscribe-to-new-block-headers)
  * [Storage queries](#storage-queries)
  * [Storage subscriptions](#storage-subscriptions)
  * [Query a mapped storage function](#query-a-mapped-storage-function)
  * [Create and send signed extrinsics](#create-and-send-signed-extrinsics)
  * [Examining the ExtrinsicReceipt object](#examining-the-extrinsicreceipt-object)
  * [ink! contract interfacing](#ink-contract-interfacing)
  * [Create mortal extrinsics](#create-mortal-extrinsics)
  * [Keypair creation and signing](#keypair-creation-and-signing)
  * [Creating keypairs with soft and hard key derivation paths](#creating-keypairs-with-soft-and-hard-key-derivation-paths)
  * [Getting estimate of network fees for extrinsic in advance](#getting-estimate-of-network-fees-for-extrinsic-in-advance)
  * [Offline signing of extrinsics](#offline-signing-of-extrinsics)
  * [Accessing runtime constants](#accessing-runtime-constants)
* [Keeping type registry presets up to date](#keeping-type-registry-presets-up-to-date)
* [Cleanup and context manager](#cleanup-and-context-manager)  
* [License](#license)

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

## Installation
```bash
pip install substrate-interface
```

## Initialization

The following examples show how to initialize for supported chains:

### Autodiscover mode

```python
substrate = SubstrateInterface(
    url="wss://rpc.polkadot.io"
)
```

When only an `url` is provided, it tries to determine certain properties like `ss58_format` and 
`type_registry_preset` automatically by calling the RPC method `system_properties`. 

At the moment this will work for Polkadot, Kusama, Kulupu and Westend nodes, for other chains the `ss58_format` 
(default 42) and  `type_registry` (defaults to latest vanilla Substrate types) should be set manually. 

### Manually set required properties

Polkadot

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

Kusama

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

Rococo

```python
substrate = SubstrateInterface(
    url="wss://rococo-rpc.polkadot.io",
    ss58_format=42,
    type_registry_preset='rococo'
)
```

Westend

```python
substrate = SubstrateInterface(
    url="wss://westend-rpc.polkadot.io",
    ss58_format=42,
    type_registry_preset='westend'
)
```

### Substrate Node Template
Compatible with https://github.com/substrate-developer-hub/substrate-node-template 

```python
substrate = SubstrateInterface(
    url="http://127.0.0.1:9933",
    ss58_format=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:

```json
{
  "runtime_id": 2,
  "types": {
    "MyCustomInt": "u32",
    "MyStruct": {
      "type": "struct",
      "type_mapping": [
         ["account", "AccountId"],
         ["message", "Vec<u8>"]
      ]
    }
  },
  "versioning": [
  ]
}
```

```python
custom_type_registry = load_type_registry_file("my-custom-types.json")

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

```

## Features

### Retrieve extrinsics for a certain block

```python
# Set block_hash to None for chaintip
block_hash = "0x51d15792ff3c5ee9c6b24ddccd95b377d5cccc759b8e76e5de9250cf58225087"

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

for extrinsic in result['extrinsics']:

    if extrinsic.address:
        signed_by_address = extrinsic.address.value
    else:
        signed_by_address = None

    print('\nPallet: {}\nCall: {}\nSigned by: {}'.format(
        extrinsic.call_module.name,
        extrinsic.call.name,
        signed_by_address
    ))

    # Loop through call params
    for param in extrinsic.params:

        if param['type'] == 'Compact<Balance>':
            param['value'] = '{} {}'.format(param['value'] / 10 ** substrate.token_decimals, substrate.token_symbol)

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

### Subscribe to new block headers

```python
def subscription_handler(obj, update_nr, subscription_id):

    print(f"New block #{obj['header']['number']} produced by {obj['author']}")

    if update_nr > 10:
        return {'message': 'Subscription will cancel when a value is returned', 'updates_processed': update_nr}


result = substrate.subscribe_block_headers(subscription_handler, include_author=True)
```

### Storage queries
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).

Example: 

```python
result = substrate.query(
    module='System',
    storage_function='Account',
    params=['F4xQKRUagnSGjFqafyhajLs94e7Vvzvr8ebwYJceKpr8R7T']
)

print(result.value['nonce']) #  7695
print(result.value['data']['free']) # 635278638077956496
```

Or get the account info at a specific block hash:

```python
account_info = substrate.query(
    module='System',
    storage_function='Account',
    params=['F4xQKRUagnSGjFqafyhajLs94e7Vvzvr8ebwYJceKpr8R7T'],
    block_hash='0x176e064454388fd78941a0bace38db424e71db9d5d5ed0272ead7003a02234fa'
)

print(account_info.value['nonce']) #  7673
print(account_info.value['data']['free']) # 637747267365404068
```

### Storage subscriptions

When a callable is passed as kwarg `subscription_handler`, there will be a subscription created for given storage query. 
Updates will be pushed to the callable and will block execution until a final value is returned. This value will be returned
as a result of the query and finally automatically unsubscribed from further updates.

```python
def subscription_handler(account_info_obj, update_nr, subscription_id):

    if update_nr == 0:
        print('Initial account data:', account_info_obj.value)

    if update_nr > 0:
        # Do something with the update
        print('Account data changed:', account_info_obj.value)

    # The execution will block until an arbitrary value is returned, which will be the result of the `query`
    if update_nr > 5:
        return account_info_obj


result = substrate.query("System", "Account", ["5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY"],
                         subscription_handler=subscription_handler)

print(result)
```

### Query a mapped storage function
Mapped storage functions can be iterated over all key/value pairs, for these type of storage functions `query_map` 
can be used.

The result is a `QueryMapResult` object, which is an iterator:

```python
# Retrieve the first 199 System.Account entries
result = substrate.query_map('System', 'Account', max_results=199)

for account, account_info in result:
    print(f"Free balance of account '{account.value}': {account_info.value['data']['free']}")
```

These results are transparantly retrieved in batches capped by the `page_size` kwarg, currently the 
maximum `page_size` restricted by the RPC node is 1000    

```python
# Retrieve all System.Account entries in batches of 200 (automatically appended by `QueryMapResult` iterator)
result = substrate.query_map('System', 'Account', page_size=200, max_results=400)

for account, account_info in result:
    print(f"Free balance of account '{account.value}': {account_info.value['data']['free']}")
```

Querying a `DoubleMap` storage function:

```python
era_stakers = substrate.query_map(
    module='Staking',
    storage_function='ErasStakers',
    params=[2100]
)
```

### Create and send signed extrinsics

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

```python
from substrateinterface import SubstrateInterface, Keypair
from substrateinterface.exceptions import SubstrateRequestException

substrate = SubstrateInterface(
    url="ws://127.0.0.1:9944",
    ss58_format=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:
    receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)
    print("Extrinsic '{}' sent and included in block '{}'".format(receipt.extrinsic_hash, receipt.block_hash))

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

The `wait_for_inclusion` keyword argument used in the example above will block giving the result until it gets 
confirmation from the node that the extrinsic is succesfully included in a block. The `wait_for_finalization` keyword
will wait until extrinsic is finalized. Note this feature is only available for websocket connections. 

### Examining the ExtrinsicReceipt object

The `substrate.submit_extrinsic` example above returns an `ExtrinsicReceipt` object, which contains information about the on-chain 
execution of the extrinsic. Because the `block_hash` is necessary to retrieve the triggered events from storage, most
information is only available when `wait_for_inclusion=True` or `wait_for_finalization=True` is used when submitting
an extrinsic. 


Examples:
```python
receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)
print(receipt.is_success) # False
print(receipt.weight) # 216625000
print(receipt.total_fee_amount) # 2749998966
print(receipt.error_message['name']) # 'LiquidityRestrictions'
```

`ExtrinsicReceipt` objects can also be created for all existing extrinsics on-chain:

```python

receipt = ExtrinsicReceipt(
    substrate=substrate,
    extrinsic_hash="0x56fea3010910bd8c0c97253ffe308dc13d1613b7e952e7e2028257d2b83c027a",
    block_hash="0x04fb003f8bc999eeb284aa8e74f2c6f63cf5bd5c00d0d0da4cd4d253a643e4c9"
)

print(receipt.is_success) # False
print(receipt.extrinsic.call_module.name) # 'Identity'
print(receipt.extrinsic.call.name) # 'remove_sub'
print(receipt.weight) # 359262000
print(receipt.total_fee_amount) # 2483332406
print(receipt.error_message['docs']) # [' Sender is not a sub-account.']

for event in receipt.triggered_events:
    print(f'* {event.value}')
```

### ink! contract interfacing

#### Deploy a contract 

_Tested on [Substrate 2.0.0-533bbbd](https://github.com/paritytech/substrate/tree/533bbbd2315d55906a6dac5726a722e094656d52) and [canvas-node](https://github.com/paritytech/canvas-node) with the [ERC20 contract from the tutorial](https://substrate.dev/substrate-contracts-workshop/#/2/introduction)_:

```python
substrate = SubstrateInterface(
    url="ws://127.0.0.1:9944",
)

keypair = Keypair.create_from_uri('//Alice')

# Upload WASM code
code = ContractCode.create_from_contract_files(
    metadata_file=os.path.join(os.path.dirname(__file__), 'erc20.json'),
    wasm_file=os.path.join(os.path.dirname(__file__), 'erc20.wasm'),
    substrate=substrate
)


# Deploy contract
contract = code.deploy(
    keypair=keypair, endowment=10 ** 15, gas_limit=1000000000000,
    constructor="new",
    args={'initial_supply': 1000 * 10 ** 15},
    upload_code=True
)

print(f'Deployed @ {contract.contract_address}')
```

#### Work with an existing instance:

```python
contract = ContractInstance.create_from_address(
    contract_address="5FV9cnzFc2tDrWcDkmoup7VZWpH9HrTaw8STnWpAQqT7KvUK",
    metadata_file=os.path.join(os.path.dirname(__file__), 'erc20.json'),
    substrate=substrate
)
```

#### Read data from a contract:

```python
result = contract.read(keypair, 'total_supply')
print('Total supply:', result.contract_result_data)
# Total supply: 1000000000000000000

result = contract.read(keypair, 'balance_of', args={'owner': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'})
print('Balance:', result.value)
# Balance: {'success': {'data': 994000000000000000, 'flags': 0, 'gas_consumed': 7251500000}}
```

#### Execute a contract call

```python
# Do a dry run of the transfer
gas_predit_result = contract.read(keypair, 'transfer', args={
    'to': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
    'value': 6 * 1000000000000000,
})

print('Result of dry-run: ', gas_predit_result.contract_result_data)
# Result of dry-run:  {'Ok': None}

print('Gas estimate: ', gas_predit_result.gas_consumed)
# Gas estimate:  24091000000

# Do the actual transfer
contract_receipt = contract.exec(keypair, 'transfer', args={
    'to': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
    'value': 6 * 1000000000000000,
}, gas_limit=gas_predit_result.gas_consumed)

if contract_receipt.is_success:
    print('Transfer success, triggered contract event:')

    for contract_event in contract_receipt.contract_events:
        print(f'* {contract_event.value}')
        # {'name': 'Transfer', 'docs': [' Event emitted when a token transfer occurs.'], 'args': [ ... ] }

    print('All triggered events:')
    for event in contract_receipt.triggered_events:
        print(f'* {event.value}')
else:
    print('ERROR: ', contract_receipt.error_message)
```


### Create mortal extrinsics

By default, _immortal_ extrinsics are created, which means they have an indefinite lifetime for being included in a 
block. However, it is recommended to use specify an expiry window, so you know after a certain amount of time if the 
extrinsic is not included in a block, it will be invalidated.

```python 
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair, era={'period': 64})
```

The `period` specifies the number of blocks the extrinsic is valid counted from current head.


### Keypair creation and signing

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

By default, a keypair is using SR25519 cryptography, alternatively ED25519 can be explictly specified:

```python
keypair = Keypair.create_from_mnemonic(mnemonic, crypto_type=KeypairType.ED25519)
```

### Creating keypairs with soft and hard key derivation paths

```python
mnemonic = Keypair.generate_mnemonic()
keypair = Keypair.create_from_uri(mnemonic + '//hard/soft')
```

By omitting the mnemonic the default development mnemonic is used: 

```python
keypair = Keypair.create_from_uri('//Alice')
```

### Getting estimate of network fees for extrinsic in advance

```python
keypair = Keypair(ss58_address="EaG2CRhJWPb7qmdcJvy3LiWdh26Jreu9Dx6R1rXxPmYXoDk")

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer',
    call_params={
        'dest': 'EaG2CRhJWPb7qmdcJvy3LiWdh26Jreu9Dx6R1rXxPmYXoDk',
        'value': 2 * 10 ** 3
    }
)
payment_info = substrate.get_payment_info(call=call, keypair=keypair)
# {'class': 'normal', 'partialFee': 2499999066, 'weight': 216625000}
```

### Offline signing of extrinsics

This example generates a signature payload which can be signed on another (offline) machine and later on sent to the 
network with the generated signature.

- Generate signature payload on online machine:
```python
substrate = SubstrateInterface(
    url="http://127.0.0.1:9933",
    ss58_format=42,
    type_registry_preset='substrate-node-template',
)

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 2 * 10**8
    }
)

era = {'period': 64, 'current': 22719}
nonce = 0

signature_payload = substrate.generate_signature_payload(call=call, era=era, nonce=nonce)
```

- Then on another (offline) machine generate the signature with given `signature_payload`:

```python
keypair = Keypair.create_from_mnemonic("nature exchange gasp toy result bacon coin broccoli rule oyster believe lyrics")
signature = keypair.sign(signature_payload)
```

- Finally on the online machine send the extrinsic with generated signature:

```python
keypair = Keypair(ss58_address="5EChUec3ZQhUvY1g52ZbfBVkqjUY9Kcr6mcEvQMbmd38shQL")

extrinsic = substrate.create_signed_extrinsic(
    call=call,
    keypair=keypair,
    era=era,
    nonce=nonce,
    signature=signature
)

result = substrate.submit_extrinsic(
    extrinsic=extrinsic
)

print(result.extrinsic_hash)
```

### Accessing runtime constants
All runtime constants are provided in the metadata (see `substrate.get_metadata_constants()`),
to access these as a decoded `ScaleType` you can use the function `substrate.get_constant()`:

```python

constant = substrate.get_constant("Balances", "ExistentialDeposit")

print(constant.value) # 10000000000
```

## Cleanup and context manager

At the end of the lifecycle of a `SubstrateInterface` instance, calling the `close()` method will do all the necessary 
cleanup, like closing the websocket connection.

When using the context manager this will be done automatically:

```python
with SubstrateInterface(url="wss://rpc.polkadot.io") as substrate:
    events = substrate.query("System", "Events")

# connection is now closed
```

## Keeping type registry presets up to date

When on-chain runtime upgrades occur, types used in call- or storage functions can be added or modified. Therefor it is
important to keep the type registry presets up to date, otherwise this can lead to decoding errors like 
`RemainingScaleBytesNotEmptyException`. 

At the moment the type registry presets for Polkadot, Kusama, Rococo and
Westend are being actively maintained for this library, and a check and update procedure can be triggered with:

```python
substrate.reload_type_registry()
```

This will also activate the updated preset for the current instance.

It is also possible to always use 
the remote type registry preset from Github with the `use_remote_preset` kwarg when instantiating:

```python
substrate = SubstrateInterface(
    url="wss://rpc.polkadot.io",
    ss58_format=0,
    type_registry_preset='polkadot',
    use_remote_preset=True
)
```

To check for updates after instantiating the `substrate` object, using `substrate.reload_type_registry()` will download 
the most recent type registry preset from Github and apply changes to current object.  

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



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/polkascan/py-substrate-interface",
    "name": "substrate-interface",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.6, <4",
    "maintainer_email": "",
    "keywords": "interface polkascan polkadot substrate blockchain rpc kusama",
    "author": "Stichting Polkascan (Polkascan Foundation)",
    "author_email": "info@polkascan.org",
    "download_url": "https://files.pythonhosted.org/packages/a0/ba/89d7cd95cff7412880d554c2658fd5f681841473a5b4529058aa6e9740ce/substrate-interface-0.13.8.tar.gz",
    "platform": "",
    "description": "# Python Substrate Interface\n\n[![Build Status](https://img.shields.io/github/workflow/status/polkascan/py-substrate-interface/Run%20unit%20tests)](https://github.com/polkascan/py-substrate-interface/actions?query=workflow%3A%22Run+unit+tests%22)\n[![Latest Version](https://img.shields.io/pypi/v/substrate-interface.svg)](https://pypi.org/project/substrate-interface/)\n[![Supported Python versions](https://img.shields.io/pypi/pyversions/substrate-interface.svg)](https://pypi.org/project/substrate-interface/)\n[![License](https://img.shields.io/pypi/l/substrate-interface.svg)](https://github.com/polkascan/py-substrate-interface/blob/master/LICENSE)\n\nPython Substrate Interface Library\n\n## Description\nThis library specializes in interfacing with a Substrate node, providing additional convenience methods to deal with\nSCALE encoding/decoding (the default output and input format of the Substrate JSONRPC), metadata parsing, type registry\nmanagement and versioning of types.\n\n## Table of Contents\n\n* [Documentation](#documentation)\n* [Installation](#installation)\n* [Initialization](#hello-world--the-flipper)\n  * [Autodiscover mode](#autodiscover-mode)\n  * [Manually set required properties](#manually-set-required-properties)  \n  * [Substrate Node Template](#substrate-node-template)\n* [Features](#features)\n  * [Get extrinsics for a certain block](#retrieve-extrinsics-for-a-certain-block)\n  * [Subscribe to new block headers](#subscribe-to-new-block-headers)\n  * [Storage queries](#storage-queries)\n  * [Storage subscriptions](#storage-subscriptions)\n  * [Query a mapped storage function](#query-a-mapped-storage-function)\n  * [Create and send signed extrinsics](#create-and-send-signed-extrinsics)\n  * [Examining the ExtrinsicReceipt object](#examining-the-extrinsicreceipt-object)\n  * [ink! contract interfacing](#ink-contract-interfacing)\n  * [Create mortal extrinsics](#create-mortal-extrinsics)\n  * [Keypair creation and signing](#keypair-creation-and-signing)\n  * [Creating keypairs with soft and hard key derivation paths](#creating-keypairs-with-soft-and-hard-key-derivation-paths)\n  * [Getting estimate of network fees for extrinsic in advance](#getting-estimate-of-network-fees-for-extrinsic-in-advance)\n  * [Offline signing of extrinsics](#offline-signing-of-extrinsics)\n  * [Accessing runtime constants](#accessing-runtime-constants)\n* [Keeping type registry presets up to date](#keeping-type-registry-presets-up-to-date)\n* [Cleanup and context manager](#cleanup-and-context-manager)  \n* [License](#license)\n\n## Documentation\nhttps://polkascan.github.io/py-substrate-interface/\n\n## Installation\n```bash\npip install substrate-interface\n```\n\n## Initialization\n\nThe following examples show how to initialize for supported chains:\n\n### Autodiscover mode\n\n```python\nsubstrate = SubstrateInterface(\n    url=\"wss://rpc.polkadot.io\"\n)\n```\n\nWhen only an `url` is provided, it tries to determine certain properties like `ss58_format` and \n`type_registry_preset` automatically by calling the RPC method `system_properties`. \n\nAt the moment this will work for Polkadot, Kusama, Kulupu and Westend nodes, for other chains the `ss58_format` \n(default 42) and  `type_registry` (defaults to latest vanilla Substrate types) should be set manually. \n\n### Manually set required properties\n\nPolkadot\n\n```python\nsubstrate = SubstrateInterface(\n    url=\"wss://rpc.polkadot.io\",\n    ss58_format=0,\n    type_registry_preset='polkadot'\n)\n```\n\nKusama\n\n```python\nsubstrate = SubstrateInterface(\n    url=\"wss://kusama-rpc.polkadot.io/\",\n    ss58_format=2,\n    type_registry_preset='kusama'\n)\n```\n\nRococo\n\n```python\nsubstrate = SubstrateInterface(\n    url=\"wss://rococo-rpc.polkadot.io\",\n    ss58_format=42,\n    type_registry_preset='rococo'\n)\n```\n\nWestend\n\n```python\nsubstrate = SubstrateInterface(\n    url=\"wss://westend-rpc.polkadot.io\",\n    ss58_format=42,\n    type_registry_preset='westend'\n)\n```\n\n### Substrate Node Template\nCompatible with https://github.com/substrate-developer-hub/substrate-node-template \n\n```python\nsubstrate = SubstrateInterface(\n    url=\"http://127.0.0.1:9933\",\n    ss58_format=42,\n    type_registry_preset='substrate-node-template'\n)\n\n```\n\nIf custom types are introduced in the Substrate chain, the following example will add compatibility by creating a custom type \nregistry JSON file and including this during initialization:\n\n```json\n{\n  \"runtime_id\": 2,\n  \"types\": {\n    \"MyCustomInt\": \"u32\",\n    \"MyStruct\": {\n      \"type\": \"struct\",\n      \"type_mapping\": [\n         [\"account\", \"AccountId\"],\n         [\"message\", \"Vec<u8>\"]\n      ]\n    }\n  },\n  \"versioning\": [\n  ]\n}\n```\n\n```python\ncustom_type_registry = load_type_registry_file(\"my-custom-types.json\")\n\nsubstrate = SubstrateInterface(\n    url=\"http://127.0.0.1:9933\",\n    ss58_format=42,\n    type_registry_preset='substrate-node-template',\n    type_registry=custom_type_registry\n)\n\n```\n\n## Features\n\n### Retrieve extrinsics for a certain block\n\n```python\n# Set block_hash to None for chaintip\nblock_hash = \"0x51d15792ff3c5ee9c6b24ddccd95b377d5cccc759b8e76e5de9250cf58225087\"\n\n# Retrieve extrinsics in block\nresult = substrate.get_block(block_hash=block_hash)\n\nfor extrinsic in result['extrinsics']:\n\n    if extrinsic.address:\n        signed_by_address = extrinsic.address.value\n    else:\n        signed_by_address = None\n\n    print('\\nPallet: {}\\nCall: {}\\nSigned by: {}'.format(\n        extrinsic.call_module.name,\n        extrinsic.call.name,\n        signed_by_address\n    ))\n\n    # Loop through call params\n    for param in extrinsic.params:\n\n        if param['type'] == 'Compact<Balance>':\n            param['value'] = '{} {}'.format(param['value'] / 10 ** substrate.token_decimals, substrate.token_symbol)\n\n        print(\"Param '{}': {}\".format(param['name'], param['value']))\n```\n\n### Subscribe to new block headers\n\n```python\ndef subscription_handler(obj, update_nr, subscription_id):\n\n    print(f\"New block #{obj['header']['number']} produced by {obj['author']}\")\n\n    if update_nr > 10:\n        return {'message': 'Subscription will cancel when a value is returned', 'updates_processed': update_nr}\n\n\nresult = substrate.subscribe_block_headers(subscription_handler, include_author=True)\n```\n\n### Storage queries\nThe modules and storage functions are provided in the metadata (see `substrate.get_metadata_storage_functions()`),\nparameters will be automatically converted to SCALE-bytes (also including decoding of SS58 addresses).\n\nExample: \n\n```python\nresult = substrate.query(\n    module='System',\n    storage_function='Account',\n    params=['F4xQKRUagnSGjFqafyhajLs94e7Vvzvr8ebwYJceKpr8R7T']\n)\n\nprint(result.value['nonce']) #  7695\nprint(result.value['data']['free']) # 635278638077956496\n```\n\nOr get the account info at a specific block hash:\n\n```python\naccount_info = substrate.query(\n    module='System',\n    storage_function='Account',\n    params=['F4xQKRUagnSGjFqafyhajLs94e7Vvzvr8ebwYJceKpr8R7T'],\n    block_hash='0x176e064454388fd78941a0bace38db424e71db9d5d5ed0272ead7003a02234fa'\n)\n\nprint(account_info.value['nonce']) #  7673\nprint(account_info.value['data']['free']) # 637747267365404068\n```\n\n### Storage subscriptions\n\nWhen a callable is passed as kwarg `subscription_handler`, there will be a subscription created for given storage query. \nUpdates will be pushed to the callable and will block execution until a final value is returned. This value will be returned\nas a result of the query and finally automatically unsubscribed from further updates.\n\n```python\ndef subscription_handler(account_info_obj, update_nr, subscription_id):\n\n    if update_nr == 0:\n        print('Initial account data:', account_info_obj.value)\n\n    if update_nr > 0:\n        # Do something with the update\n        print('Account data changed:', account_info_obj.value)\n\n    # The execution will block until an arbitrary value is returned, which will be the result of the `query`\n    if update_nr > 5:\n        return account_info_obj\n\n\nresult = substrate.query(\"System\", \"Account\", [\"5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY\"],\n                         subscription_handler=subscription_handler)\n\nprint(result)\n```\n\n### Query a mapped storage function\nMapped storage functions can be iterated over all key/value pairs, for these type of storage functions `query_map` \ncan be used.\n\nThe result is a `QueryMapResult` object, which is an iterator:\n\n```python\n# Retrieve the first 199 System.Account entries\nresult = substrate.query_map('System', 'Account', max_results=199)\n\nfor account, account_info in result:\n    print(f\"Free balance of account '{account.value}': {account_info.value['data']['free']}\")\n```\n\nThese results are transparantly retrieved in batches capped by the `page_size` kwarg, currently the \nmaximum `page_size` restricted by the RPC node is 1000    \n\n```python\n# Retrieve all System.Account entries in batches of 200 (automatically appended by `QueryMapResult` iterator)\nresult = substrate.query_map('System', 'Account', page_size=200, max_results=400)\n\nfor account, account_info in result:\n    print(f\"Free balance of account '{account.value}': {account_info.value['data']['free']}\")\n```\n\nQuerying a `DoubleMap` storage function:\n\n```python\nera_stakers = substrate.query_map(\n    module='Staking',\n    storage_function='ErasStakers',\n    params=[2100]\n)\n```\n\n### Create and send signed extrinsics\n\nThe following code snippet illustrates how to create a call, wrap it in a signed extrinsic and send it to the network:\n\n```python\nfrom substrateinterface import SubstrateInterface, Keypair\nfrom substrateinterface.exceptions import SubstrateRequestException\n\nsubstrate = SubstrateInterface(\n    url=\"ws://127.0.0.1:9944\",\n    ss58_format=42,\n    type_registry_preset='kusama'\n)\n\nkeypair = Keypair.create_from_mnemonic('episode together nose spoon dose oil faculty zoo ankle evoke admit walnut')\n\ncall = substrate.compose_call(\n    call_module='Balances',\n    call_function='transfer',\n    call_params={\n        'dest': '5E9oDs9PjpsBbxXxRE9uMaZZhnBAV38n2ouLB28oecBDdeQo',\n        'value': 1 * 10**12\n    }\n)\n\nextrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)\n\ntry:\n    receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)\n    print(\"Extrinsic '{}' sent and included in block '{}'\".format(receipt.extrinsic_hash, receipt.block_hash))\n\nexcept SubstrateRequestException as e:\n    print(\"Failed to send: {}\".format(e))\n```\n\nThe `wait_for_inclusion` keyword argument used in the example above will block giving the result until it gets \nconfirmation from the node that the extrinsic is succesfully included in a block. The `wait_for_finalization` keyword\nwill wait until extrinsic is finalized. Note this feature is only available for websocket connections. \n\n### Examining the ExtrinsicReceipt object\n\nThe `substrate.submit_extrinsic` example above returns an `ExtrinsicReceipt` object, which contains information about the on-chain \nexecution of the extrinsic. Because the `block_hash` is necessary to retrieve the triggered events from storage, most\ninformation is only available when `wait_for_inclusion=True` or `wait_for_finalization=True` is used when submitting\nan extrinsic. \n\n\nExamples:\n```python\nreceipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)\nprint(receipt.is_success) # False\nprint(receipt.weight) # 216625000\nprint(receipt.total_fee_amount) # 2749998966\nprint(receipt.error_message['name']) # 'LiquidityRestrictions'\n```\n\n`ExtrinsicReceipt` objects can also be created for all existing extrinsics on-chain:\n\n```python\n\nreceipt = ExtrinsicReceipt(\n    substrate=substrate,\n    extrinsic_hash=\"0x56fea3010910bd8c0c97253ffe308dc13d1613b7e952e7e2028257d2b83c027a\",\n    block_hash=\"0x04fb003f8bc999eeb284aa8e74f2c6f63cf5bd5c00d0d0da4cd4d253a643e4c9\"\n)\n\nprint(receipt.is_success) # False\nprint(receipt.extrinsic.call_module.name) # 'Identity'\nprint(receipt.extrinsic.call.name) # 'remove_sub'\nprint(receipt.weight) # 359262000\nprint(receipt.total_fee_amount) # 2483332406\nprint(receipt.error_message['docs']) # [' Sender is not a sub-account.']\n\nfor event in receipt.triggered_events:\n    print(f'* {event.value}')\n```\n\n### ink! contract interfacing\n\n#### Deploy a contract \n\n_Tested on [Substrate 2.0.0-533bbbd](https://github.com/paritytech/substrate/tree/533bbbd2315d55906a6dac5726a722e094656d52) and [canvas-node](https://github.com/paritytech/canvas-node) with the [ERC20 contract from the tutorial](https://substrate.dev/substrate-contracts-workshop/#/2/introduction)_:\n\n```python\nsubstrate = SubstrateInterface(\n    url=\"ws://127.0.0.1:9944\",\n)\n\nkeypair = Keypair.create_from_uri('//Alice')\n\n# Upload WASM code\ncode = ContractCode.create_from_contract_files(\n    metadata_file=os.path.join(os.path.dirname(__file__), 'erc20.json'),\n    wasm_file=os.path.join(os.path.dirname(__file__), 'erc20.wasm'),\n    substrate=substrate\n)\n\n\n# Deploy contract\ncontract = code.deploy(\n    keypair=keypair, endowment=10 ** 15, gas_limit=1000000000000,\n    constructor=\"new\",\n    args={'initial_supply': 1000 * 10 ** 15},\n    upload_code=True\n)\n\nprint(f'Deployed @ {contract.contract_address}')\n```\n\n#### Work with an existing instance:\n\n```python\ncontract = ContractInstance.create_from_address(\n    contract_address=\"5FV9cnzFc2tDrWcDkmoup7VZWpH9HrTaw8STnWpAQqT7KvUK\",\n    metadata_file=os.path.join(os.path.dirname(__file__), 'erc20.json'),\n    substrate=substrate\n)\n```\n\n#### Read data from a contract:\n\n```python\nresult = contract.read(keypair, 'total_supply')\nprint('Total supply:', result.contract_result_data)\n# Total supply: 1000000000000000000\n\nresult = contract.read(keypair, 'balance_of', args={'owner': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'})\nprint('Balance:', result.value)\n# Balance: {'success': {'data': 994000000000000000, 'flags': 0, 'gas_consumed': 7251500000}}\n```\n\n#### Execute a contract call\n\n```python\n# Do a dry run of the transfer\ngas_predit_result = contract.read(keypair, 'transfer', args={\n    'to': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',\n    'value': 6 * 1000000000000000,\n})\n\nprint('Result of dry-run: ', gas_predit_result.contract_result_data)\n# Result of dry-run:  {'Ok': None}\n\nprint('Gas estimate: ', gas_predit_result.gas_consumed)\n# Gas estimate:  24091000000\n\n# Do the actual transfer\ncontract_receipt = contract.exec(keypair, 'transfer', args={\n    'to': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',\n    'value': 6 * 1000000000000000,\n}, gas_limit=gas_predit_result.gas_consumed)\n\nif contract_receipt.is_success:\n    print('Transfer success, triggered contract event:')\n\n    for contract_event in contract_receipt.contract_events:\n        print(f'* {contract_event.value}')\n        # {'name': 'Transfer', 'docs': [' Event emitted when a token transfer occurs.'], 'args': [ ... ] }\n\n    print('All triggered events:')\n    for event in contract_receipt.triggered_events:\n        print(f'* {event.value}')\nelse:\n    print('ERROR: ', contract_receipt.error_message)\n```\n\n\n### Create mortal extrinsics\n\nBy default, _immortal_ extrinsics are created, which means they have an indefinite lifetime for being included in a \nblock. However, it is recommended to use specify an expiry window, so you know after a certain amount of time if the \nextrinsic is not included in a block, it will be invalidated.\n\n```python \nextrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair, era={'period': 64})\n```\n\nThe `period` specifies the number of blocks the extrinsic is valid counted from current head.\n\n\n### Keypair creation and signing\n\n```python\nmnemonic = Keypair.generate_mnemonic()\nkeypair = Keypair.create_from_mnemonic(mnemonic)\nsignature = keypair.sign(\"Test123\")\nif keypair.verify(\"Test123\", signature):\n    print('Verified')\n```\n\nBy default, a keypair is using SR25519 cryptography, alternatively ED25519 can be explictly specified:\n\n```python\nkeypair = Keypair.create_from_mnemonic(mnemonic, crypto_type=KeypairType.ED25519)\n```\n\n### Creating keypairs with soft and hard key derivation paths\n\n```python\nmnemonic = Keypair.generate_mnemonic()\nkeypair = Keypair.create_from_uri(mnemonic + '//hard/soft')\n```\n\nBy omitting the mnemonic the default development mnemonic is used: \n\n```python\nkeypair = Keypair.create_from_uri('//Alice')\n```\n\n### Getting estimate of network fees for extrinsic in advance\n\n```python\nkeypair = Keypair(ss58_address=\"EaG2CRhJWPb7qmdcJvy3LiWdh26Jreu9Dx6R1rXxPmYXoDk\")\n\ncall = substrate.compose_call(\n    call_module='Balances',\n    call_function='transfer',\n    call_params={\n        'dest': 'EaG2CRhJWPb7qmdcJvy3LiWdh26Jreu9Dx6R1rXxPmYXoDk',\n        'value': 2 * 10 ** 3\n    }\n)\npayment_info = substrate.get_payment_info(call=call, keypair=keypair)\n# {'class': 'normal', 'partialFee': 2499999066, 'weight': 216625000}\n```\n\n### Offline signing of extrinsics\n\nThis example generates a signature payload which can be signed on another (offline) machine and later on sent to the \nnetwork with the generated signature.\n\n- Generate signature payload on online machine:\n```python\nsubstrate = SubstrateInterface(\n    url=\"http://127.0.0.1:9933\",\n    ss58_format=42,\n    type_registry_preset='substrate-node-template',\n)\n\ncall = substrate.compose_call(\n    call_module='Balances',\n    call_function='transfer',\n    call_params={\n        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',\n        'value': 2 * 10**8\n    }\n)\n\nera = {'period': 64, 'current': 22719}\nnonce = 0\n\nsignature_payload = substrate.generate_signature_payload(call=call, era=era, nonce=nonce)\n```\n\n- Then on another (offline) machine generate the signature with given `signature_payload`:\n\n```python\nkeypair = Keypair.create_from_mnemonic(\"nature exchange gasp toy result bacon coin broccoli rule oyster believe lyrics\")\nsignature = keypair.sign(signature_payload)\n```\n\n- Finally on the online machine send the extrinsic with generated signature:\n\n```python\nkeypair = Keypair(ss58_address=\"5EChUec3ZQhUvY1g52ZbfBVkqjUY9Kcr6mcEvQMbmd38shQL\")\n\nextrinsic = substrate.create_signed_extrinsic(\n    call=call,\n    keypair=keypair,\n    era=era,\n    nonce=nonce,\n    signature=signature\n)\n\nresult = substrate.submit_extrinsic(\n    extrinsic=extrinsic\n)\n\nprint(result.extrinsic_hash)\n```\n\n### Accessing runtime constants\nAll runtime constants are provided in the metadata (see `substrate.get_metadata_constants()`),\nto access these as a decoded `ScaleType` you can use the function `substrate.get_constant()`:\n\n```python\n\nconstant = substrate.get_constant(\"Balances\", \"ExistentialDeposit\")\n\nprint(constant.value) # 10000000000\n```\n\n## Cleanup and context manager\n\nAt the end of the lifecycle of a `SubstrateInterface` instance, calling the `close()` method will do all the necessary \ncleanup, like closing the websocket connection.\n\nWhen using the context manager this will be done automatically:\n\n```python\nwith SubstrateInterface(url=\"wss://rpc.polkadot.io\") as substrate:\n    events = substrate.query(\"System\", \"Events\")\n\n# connection is now closed\n```\n\n## Keeping type registry presets up to date\n\nWhen on-chain runtime upgrades occur, types used in call- or storage functions can be added or modified. Therefor it is\nimportant to keep the type registry presets up to date, otherwise this can lead to decoding errors like \n`RemainingScaleBytesNotEmptyException`. \n\nAt the moment the type registry presets for Polkadot, Kusama, Rococo and\nWestend are being actively maintained for this library, and a check and update procedure can be triggered with:\n\n```python\nsubstrate.reload_type_registry()\n```\n\nThis will also activate the updated preset for the current instance.\n\nIt is also possible to always use \nthe remote type registry preset from Github with the `use_remote_preset` kwarg when instantiating:\n\n```python\nsubstrate = SubstrateInterface(\n    url=\"wss://rpc.polkadot.io\",\n    ss58_format=0,\n    type_registry_preset='polkadot',\n    use_remote_preset=True\n)\n```\n\nTo check for updates after instantiating the `substrate` object, using `substrate.reload_type_registry()` will download \nthe most recent type registry preset from Github and apply changes to current object.  \n\n## License\nhttps://github.com/polkascan/py-substrate-interface/blob/master/LICENSE\n\n\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Library for interfacing with a Substrate node",
    "version": "0.13.8",
    "split_keywords": [
        "interface",
        "polkascan",
        "polkadot",
        "substrate",
        "blockchain",
        "rpc",
        "kusama"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "5b19bf75fb570e010b313b8eced099ea",
                "sha256": "731aecf4c521ddcd6fe1e25a644162301ee88102042d7af37547a03e622f0387"
            },
            "downloads": -1,
            "filename": "substrate_interface-0.13.8-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5b19bf75fb570e010b313b8eced099ea",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.6, <4",
            "size": 172148,
            "upload_time": "2021-06-08T12:16:30",
            "upload_time_iso_8601": "2021-06-08T12:16:30.695591Z",
            "url": "https://files.pythonhosted.org/packages/cb/d4/164c1d653bfd8acf96350adce815fe0052551a04c9601ae66b98993617af/substrate_interface-0.13.8-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "b4d4b931d5be0ff0f1c8bbf0ef82579e",
                "sha256": "48ece53d2efc9ba8e6a4e7fc2bda16c2e7de849585ac80c31cfc3afbd74b1f9e"
            },
            "downloads": -1,
            "filename": "substrate-interface-0.13.8.tar.gz",
            "has_sig": false,
            "md5_digest": "b4d4b931d5be0ff0f1c8bbf0ef82579e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6, <4",
            "size": 165297,
            "upload_time": "2021-06-08T12:16:34",
            "upload_time_iso_8601": "2021-06-08T12:16:34.322523Z",
            "url": "https://files.pythonhosted.org/packages/a0/ba/89d7cd95cff7412880d554c2658fd5f681841473a5b4529058aa6e9740ce/substrate-interface-0.13.8.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2021-06-08 12:16:34",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": null,
    "github_project": "polkascan",
    "error": "Could not fetch GitHub repository",
    "lcname": "substrate-interface"
}
        
Elapsed time: 0.29738s