# Py substrate interface

## Python Substrate Interface <a href="#python-substrate-interface" id="python-substrate-interface"></a>

### Introduction <a href="#introduction" id="introduction"></a>

[Python Substrate Interface](https://github.com/polkascan/py-substrate-interface) library allows application developers to query a Phron node and interact with the node's Polkadot or Substrate features using a native Python interface. Here you will find an overview of the available functionalities and some commonly used code examples to get you started on interacting with Phron networks using Python Substrate Interface.

### Checking Prerequisites <a href="#checking-prerequisites" id="checking-prerequisites"></a>

For the examples in this guide, you will need to have the following:

* An account with funds. You can get DEV tokens for testing on Phron once every 24 hours from the [Phron Faucet](https://faucet.phron.ai/)
* To test out the examples in this guide on Phron, you will need to have your own endpoint and API key, which you can get from one of the supported Endpoint Providers
* Have [`pip`](https://pypi.org/project/pip) installed

> ## Note !
>
> The examples in this guide assume you have a MacOS or Ubuntu 22.04-based environment and will need to be adapted accordingly for Windows.

#### Installing Python Substrate Interface <a href="#installing-python-substrate-interface" id="installing-python-substrate-interface"></a>

You can install Python Substrate Interface library for your project through `pip`. Run the following command in your project directory:

```bash
pip install substrate-interface
```

### Creating an API Provider Instance <a href="#creating-an-api-provider-instance" id="creating-an-api-provider-instance"></a>

Similar to ETH API libraries, you must first instantiate an API instance of Python Substrate Interface API. Create the `WsProvider` using the websocket endpoint of the Phron network you wish to interact with.

To test out the examples in this guide on Phron, you will need to have your own endpoint and API key, which you can get from one of the supported Endpoint Providers.

{% tabs %}
{% tab title="Phron" %}

```rust
# Imports
from substrateinterface import SubstrateInterface

# Construct the API provider
ws_provider = SubstrateInterface(
    url="INSERT_WSS_API_ENDPOINT",
)
```

{% endtab %}

{% tab title="Phron Dev Node" %}

```javascript
# Import
from substrateinterface import SubstrateInterface

# Construct the API provider
ws_provider = SubstrateInterface(
    url="ws://127.0.0.1:9944",
)
```

{% endtab %}
{% endtabs %}

### Querying for Information <a href="#querying-for-information" id="querying-for-information"></a>

In this section, you will learn how to query for on-chain information of Phron networks using Python Substrate Interface library.

#### Accessing Runtime Constants <a href="#accessing-runtime-constants" id="accessing-runtime-constants"></a>

All runtime constants, such as `BlockWeights`, `DefaultBlocksPerRound` and `ExistentialDeposit`, are provided in the metadata. You can use the [`get_metadata_constants`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.get_metadata_constants) method to see a list of available runtime constants within Phron network's metadata.

Runtime constants available in the metadata can be queried through the [`get_constant`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.get_constant) method.

```javascript
# Imports
from substrateinterface import SubstrateInterface

# Construct the API provider
ws_provider = SubstrateInterface(
    url="wss://testnet.phron.ai",
)   

# List of available runtime constants in the metadata
constant_list = ws_provider.get_metadata_constants()
print(constant_list)

# Retrieve the Existential Deposit constant on Moonbeam, which is 0
constant = ws_provider.get_constant("Balances", "ExistentialDeposit")
print(constant.value)
```

#### Retrieving Blocks and Extrinsics <a href="#retrieving-blocks-and-extrinsics" id="retrieving-blocks-and-extrinsics"></a>

You can retrieve basic information about Phron networks, such as blocks and extrinsics, using the Python Substrate Interface API.

To retrieve a block, you can use the [`get_block`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.get_block) method. You can also access extrinsics and their data fields inside a block object, which is simply a Python dictionary.

To retrieve a block header, you can use the [`get_block_header`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.get_block_header) method.

```javascript
# Imports
from substrateinterface import SubstrateInterface

# Construct the API provider
ws_provider = SubstrateInterface(
    url="wss://testnet.phron.ai",
)

# Retrieve the latest block
block = ws_provider.get_block()

# Retrieve the latest finalized block
block = ws_provider.get_block_header(finalized_only=True)

# Retrieve a block given its Substrate block hash
block_hash = "0xa499d4ebccdabe31218d232460c0f8b91bd08f72aca25f9b25b04b6dfb7a2acb"
block = ws_provider.get_block(block_hash=block_hash)

# Iterate through the extrinsics inside the block
for extrinsic in block["extrinsics"]:
    if "address" in extrinsic:
        signed_by_address = extrinsic["address"].value
    else:
        signed_by_address = None
    print(
        "\nPallet: {}\nCall: {}\nSigned by: {}".format(
            extrinsic["call"]["call_module"].name,
            extrinsic["call"]["call_function"].name,
            signed_by_address,
        )
    )
```

> ## Note!
>
> The block hash used in the above code sample is the Substrate block hash. The standard methods in Python Substrate Interface assume you are using the Substrate version of primitives, such as block or tx hashes.

#### Subscribing to New Block Headers <a href="#subscribing-to-new-block-headers" id="subscribing-to-new-block-headers"></a>

You can also adapt the previous example to use a subscription based model to listen to new block headers.

```javascript
# Imports
from substrateinterface import SubstrateInterface

# Construct the API provider
ws_provider = SubstrateInterface(
    url="wss://testnet.phron.ai",
)


def subscription_handler(obj, update_nr, subscription_id):
    print(f"New block #{obj['header']['number']}")

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


result = ws_provider.subscribe_block_headers(subscription_handler)
```

#### Querying for Storage Information <a href="#querying-for-storage-information" id="querying-for-storage-information"></a>

You can use the [`get_metadata_storage_functions`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.get_metadata_storage_functions) to see a list of available storage functions within Phron network's metadata.

Chain states that are provided in the metadata through storage functions can be queried through the [`query`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.query) method.

The Substrate system modules, such as `System`, `Timestamp`, and `Balances`, can be queried to provide basic information such as account nonce and balance. The available storage functions are read from the metadata dynamically, so you can also query for storage information on Phron custom modules, such as `ParachainStaking` and `Democracy`, for state information that's specific to Phron.

```javascript
# Imports
from substrateinterface import SubstrateInterface

# Construct the API provider
ws_provider = SubstrateInterface(
    url="wss://testnet.phron.ai",
)

# List of available storage functions in the metadata
method_list = ws_provider.get_metadata_storage_functions()
print(method_list)

# Query basic account information
account_info = ws_provider.query(
    module="System",
    storage_function="Account",
    params=["0x578002f699722394afc52169069a1FfC98DA36f1"],
)
# Log the account nonce
print(account_info.value["nonce"])
# Log the account free balance
print(account_info.value["data"]["free"])

# Query candidate pool information from Moonbeam's Parachain Staking module
candidate_pool_info = ws_provider.query(
    module="ParachainStaking", storage_function="CandidatePool", params=[]
)
print(candidate_pool_info)
```

### Signing and Transactions <a href="#signing-and-transactions" id="signing-and-transactions"></a>

#### Creating a Keypair <a href="#creating-a-keypair" id="creating-a-keypair"></a>

The keypair object in Python Substrate Interface is used in the signing of any data, whether it's a transfer, a message, or a contract interaction.

You can create a keypair instance from the shortform private key or from the mnemonic. For Phron networks, you also need to specify the `KeypairType` to be `KeypairType.ECDSA`.

```javascript
# Imports
from substrateinterface import Keypair, KeypairType

# Define the shortform private key
privatekey = bytes.fromhex("INSERT_PRIVATE_KEY_WITHOUT_0X_PREFIX")

# Define the account mnenomic
mnemonic = "INSERT_MNEMONIC"

# Generate the keypair from shortform private key
keypair = Keypair.create_from_private_key(privatekey, crypto_type=KeypairType.ECDSA)

# Generate the keypair from mnemonic
keypair = Keypair.create_from_mnemonic(mnemonic, crypto_type=KeypairType.ECDSA)
```

#### Forming and Sending a Transaction <a href="#forming-and-sending-a-transaction" id="forming-and-sending-a-transaction"></a>

The [`compose_call`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.compose_call) method can be used to compose a call payload which can be used as an unsigned extrinsic or a proposal.

Then the payload can be signed using a keypair through the [`create_signed_extrinsic`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.create_signed_extrinsic) method.

The signed extrinsic can then be submitted using the [`submit_extrinsic`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.submit_extrinsic) method.

This method will also return an `ExtrinsicReceipt` object which contains information about the on-chain execution of the extrinsic. If you need to examine the receipt object, you can set the `wait_for_inclusion` to `True` when submitting the extrinsic to wait until the extrinsic is successfully included into the block.

The following sample code will show a complete example for sending a transaction.

```javascript
# Imports
from substrateinterface import SubstrateInterface, Keypair, KeypairType
from substrateinterface.exceptions import SubstrateRequestException

# Construct the API provider
ws_provider = SubstrateInterface(
    url="wss://testnet.phron.ai",
)

# Define the shortform private key of the sending account
privatekey = bytes.fromhex("INSERT_PRIVATE_KEY_WITHOUT_0X_PREFIX")

# Generate the keypair
keypair = Keypair.create_from_private_key(privatekey, crypto_type=KeypairType.ECDSA)

# Form a transaction call
call = ws_provider.compose_call(
    call_module="Balances",
    call_function="transfer_allow_death",
    call_params={
        "dest": "0x44236223aB4291b93EEd10E4B511B37a398DEE55",
        "value": 1 * 10**18,
    },
)

# Form a signed extrinsic
extrinsic = ws_provider.create_signed_extrinsic(call=call, keypair=keypair)

# Submit the extrinsic
try:
    receipt = ws_provider.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))
```

#### Offline Signing <a href="#offline-signing" id="offline-signing"></a>

You can sign transaction payloads or any arbitrary data using a keypair object through the [`sign`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.Keypair.sign) method. This can be used for offline signing of transactions.

1. First, generate the signature payload on an online machine:

   ```javascript
   # Imports
   from substrateinterface import SubstrateInterface

   # Construct the API provider
   ws_provider = SubstrateInterface(
       url="wss://testnet.phron.ai",
   )

   # Construct a transaction call
   call = ws_provider.compose_call(
       call_module="Balances",
       call_function="transfer_allow_death",
       call_params={
           "dest": "0x44236223aB4291b93EEd10E4B511B37a398DEE55",
           "value": 1 * 10**18,
       },
   )

   # Generate the signature payload
   signature_payload = ws_provider.generate_signature_payload(call=call)
   ```
2. On an offline machine, create a keypair with the private key of the sending account, and sign the signature payload:

   <pre class="language-javascript" data-overflow="wrap"><code class="lang-javascript"># Imports
   from substrateinterface import Keypair, KeypairType

   # Define the signature payload from the offline machine
   signature_payload = "INSERT_SIGNATURE_PAYLOAD"

   # Define the shortform private key of the sender account
   privatekey = bytes.fromhex("INSERT_PRIVATE_KEY_WITHOUT_0X_PREFIX")

   # Generate the keypair from shortform private key
   keypair = Keypair.create_from_private_key(privatekey, crypto_type=KeypairType.ECDSA)

   # Sign the signature_payload 
   signature = keypair.sign(signature_payload)
   </code></pre>
3. On an online machine, create a keypair with the public key of the sending account, and submit the extrinsic with the generated signature from the offline machine:

   <pre class="language-javascript" data-overflow="wrap"><code class="lang-javascript"># Imports
   from substrateinterface import SubstrateInterface, Keypair, KeypairType

   # Construct the API provider
   ws_provider = SubstrateInterface(
       url="wss://testnet.phron.ai",
   )

   # Define the signature from the offline machine
   signature_payload = "INSERT_SIGNATURE_PAYLOAD"

   # Construct a keypair with the Ethereum style wallet address of the sending account
   keypair = Keypair(public_key="INSERT_ADDRESS_WITHOUT_0X", crypto_type=KeypairType.ECDSA)

   # Construct the same transaction call that was signed
   call = ws_provider.compose_call(
       call_module="Balances",
       call_function="transfer_allow_death",
       call_params={
           "dest": "0x44236223aB4291b93EEd10E4B511B37a398DEE55",
           "value": 1 * 10**18,
       },
   )

   # Construct the signed extrinsic with the generated signature
   extrinsic = ws_provider.create_signed_extrinsic(
       call=call, keypair=keypair, signature=signature
   )

   # Submit the signed extrinsic
   result = ws_provider.submit_extrinsic(extrinsic=extrinsic)

   # Print the execution result
   print(result.extrinsic_hash)
   </code></pre>

### Custom RPC Requests <a href="#custom-rpc-requests" id="custom-rpc-requests"></a>

You can also make custom RPC requests with the [`rpc_request`](https://polkascan.github.io/py-substrate-interface/#substrateinterface.SubstrateInterface.rpc_request) method.

This is particularly useful for interacting with Phron's Ethereum JSON-RPC endpoints or Phron's custom RPC endpoints.

The Consensus and Finality page has examples for using the custom RPC calls through Python Substrate Interface to check the finality of a transaction given its transaction hash.

This tutorial is for educational purposes only. As such, any contracts or code created in this tutorial should not be used in production.The information presented herein has been provided by third parties and is made available solely for general information purposes. Phron does not endorse any project listed and described on the Phron Doc Website (<https://docs.Phron.ai/>). Phron does not warrant the accuracy, completeness or usefulness of this information. Any reliance you place on such information is strictly at your own risk. Phron disclaims all liability and responsibility arising from any reliance placed on this information by you or by anyone who may be informed of any of its contents. All statements and/or opinions expressed in these materials are solely the responsibility of the person or entity providing those materials and do not necessarily represent the opinion of Phron. The information should not be construed as professional or financial advice of any kind. Advice from a suitably qualified professional should always be sought in relation to any particular matter or circumstance. The information herein may link to or integrate with other websites operated or content provided by third parties, and such other websites may link to this website. Phron has no control over any such other websites or their content and will have no liability arising out of or related to such websites or their content. The existence of any such link does not constitute an endorsement of such websites, the content of the websites, or the operators of the websites. These links are being provided to you only as a convenience and you release and hold Phron harmless from any and all liability arising from your use of this information or the information provided by any third-party website or service.
