substrate-interface

repository·master·Indexed 42 days ago

https://github.com/jamdottech/py-polkadot-sdk

A Python library for interfacing with Substrate-based blockchain nodes. It provides tools to query storage, compose and sign extrinsics, handle SCALE encoding/decoding, and interact with ink! smart contracts. Features include support for embedded light clients, RPC endpoints, multisig transactions, and an extension framework for integrating third-party search indices like Polkascan and Subsquid.

Tokens
18.7K
Snippets
62
Records
71
Agent score
54%

What's inside substrate-interface

  1. Overview of py-polkadot-sdk capabilities

    master

    The py-polkadot-sdk library is designed for interfacing with Substrate nodes. It provides specialized tools for:

    • Querying node storage.
    • Composing extrinsics (transactions).
    • SCALE encoding and decoding.
    • Providing convenience methods for Substrate runtime features and metadata.
  2. Understand type decomposition of call parameters

    master

    When working with extrinsics, call parameters are often represented as complex, nested structures. The library provides a way to decompose these types to understand the required schema for specific pallet calls. For example, a currency_id might be a simple string or a complex object depending on the pallet and the specific token type (e.g., DexShare, Erc20, StableAssetPoolToken).

    Commonly encountered types in parameter decomposition include:

    • Token identifiers: Can be a simple string (e.g., 'DOT', 'KSM') or a structured object.
    • Account identifiers: Often involve nested structures like AccountId32 which contains network information (Polkadot, Kusama, etc.) and the raw id ([u8; 32]).
    • Plurality/Governance types: Complex structures defining membership, fractions, or proportions (e.g., nom, denom).
    {
      'currency_id': {
        'Token': ('ACA', 'AUSD', 'DOT', 'LDOT', 'TAP', 'RENBTC', 'CASH', 'KAR', 'KUSD', 'KSM', 'LKSM', 'TAI', 'BNC', 'VSKSM', 'PHA', 'KINT', 'KBTC'),
        'DexShare': ({
          'Token': ('ACA', 'AUSD', 'DOT', 'LDOT', 'TAP', 'RENBTC', 'CASH', 'KAR', 'KUSD', 'KSM', 'LKSM', 'TAI', 'BNC', 'VSKSM', 'PHA', 'KINT', 'KBTC'),
          'Erc20': '[u8; 20]',
          'LiquidCrowdloan': 'u32',
          'ForeignAsset': 'u16',
          'StableAssetPoolToken': 'u32'
        }, {
          'Token': ('ACA', 'AUSD', 'DOT', 'LDOT', 'TAP', 'RENBTC', 'CASH', 'KAR', 'KUSD', 'KSM', 'LKSM', 'TAI', 'BNC', 'VSKSM', 'PHA', 'KINT', 'KBTC'),
          'Erc20': '[u8; 20]',
          'LiquidCrowdloan': 'u32',
          'ForeignAsset': 'u16',
          'StableAssetPoolToken': 'u32'
        }),
        'Erc20': '[u8; 20]',
        'StableAssetPoolToken': 'u32'
      }
    }
  3. Create Keypairs using derivation paths (URI)

    master

    You can create keypairs using a URI string that includes a mnemonic and a derivation path.

    • Soft/Hard derivation: Use //hard/soft suffixes to specify paths.
    • Default mnemonic: If you omit the mnemonic and only provide a path like //Alice, the default development mnemonic is used.
    • BIP44 derivation: For Ethereum-style addresses, use the standard BIP44 path format (e.g., /m/44'/60'/0'/0/0) combined with KeypairType.ECDSA.
    # Soft/Hard derivation
    Mnemonic = Keypair.generate_mnemonic()
    keypair = Keypair.create_from_uri(mnemonic + '//hard/soft')
    
    # Default development mnemonic
    keypair = Keypair.create_from_uri('//Alice')
    
    # BIP44 derivation for ECDSA
    Mnemonic = Keypair.generate_mnemonic()
    keypair = Keypair.create_from_uri(f"{mnemonic}/m/44'/60'/0'/0/0", crypto_type=KeypairType.ECDSA)
  4. How the extension framework works

    master
    The extension framework enhances search capabilities beyond the standard functionality provided by a Substrate node. It enables the integration of third-party search indices (such as Polkascan or Subsquid). These extensions are interchangeable as long as they follow standardized naming conventions in the extension registry, allowing you to swap data sources that provide similar functionality.
  5. Create and submit signed extrinsics

    master

    In Substrate, extrinsics are vehicles used to execute function calls in the runtime. To execute a call, you must first compose it using compose_call, sign it with a keypair using create_signed_extrinsic, and then submit it via submit_extrinsic.

    When submitting, you can use the following arguments to control execution flow:

    • wait_for_inclusion=True: Blocks until the node confirms the extrinsic is included in a block. This is required to access detailed ExtrinsicReceipt information.
    • wait_for_finalization=True: Blocks until the extrinsic is finalized (only available for websocket connections).

    If submission fails, a SubstrateRequestException is raised.

    call = substrate.compose_call(
        call_module='Balances',
        call_function='transfer_keep_alive',
        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(f"Extrinsic '{receipt.extrinsic_hash}' sent and included in block '{receipt.block_hash}'")
    except SubstrateRequestException as e:
        print("Failed to send: {}".format(e))
  6. Deploy an ink! contract

    master

    To deploy a new ink! smart contract, use the ContractCode.create_from_contract_files method to prepare the code from your metadata and WASM files, then call .deploy() on the resulting object.

    Key parameters for .deploy():

    • keypair: The account used to pay for deployment.
    • endowment: The amount of funds provided to the contract for initial setup.
    • gas_limit: The maximum gas allowed for the deployment transaction.
    • constructor: The name of the constructor function to call (e.g., 'new').
    • args: A dictionary of arguments to pass to the constructor.
    • upload_code: Boolean indicating whether to upload the WASM code to the chain.
    substrate = SubstrateInterface(
        url="ws://127.0.0.1:9944",
        type_registry_preset='canvas'
    )
    
    keypair = Keypair.create_from_uri('//Alice')
    
    # Prepare contract code from files
    code = ContractCode.create_from_contract_files(
        metadata_file=os.path.join(os.path.dirname(__file__), 'assets', 'flipper.json'),
        wasm_file=os.path.join(os.path.dirname(__file__), 'assets', 'flipper.wasm'),
        substrate=substrate
    )
    
    # Deploy the contract
    contract = code.deploy(
        keypair=keypair,
        endowment=0,
        gas_limit=1000000000000,
        constructor="new",
        args={'init_value': True},
        upload_code=True
    )
    
    print(f'✅ Deployed @ {contract.contract_address}')
  7. Handle multisig extrinsics

    master

    Multisig allows multiple accounts to coordinate a single call based on a set of signatories and a required threshold.

    To use multisig:

    1. Generate a multisig account: Use substrate.generate_multisig_account(signatories, threshold) to create a deterministic account representation.
    2. Initiate the multisig: Use substrate.create_multisig_extrinsic(call, keypair, multisig_account, era) where keypair is one of the signatories. The era parameter (e.g., {'period': 64}) defines the validity period.
    3. Finalize the multisig: A second (or subsequent) signatory must call create_multisig_extrinsic with the same call and multisig_account. The call is executed on-chain once the threshold is met.

    The call is processed and executed when the final required signatory submits their extrinsic.

    # 1. Define the multisig account
    keypair_alice = Keypair.create_from_uri('//Alice', ss58_format=substrate.ss58_format)
    multisig_account = substrate.generate_multisig_account(
        signatories=[
            keypair_alice.ss58_address, 
            '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', 
            '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y'
        ], 
        threshold=2
    )
    
    # 2. Initiate the multisig extrinsic
    call = substrate.compose_call(
        call_module='System',
        call_function='remark_with_event',
        call_params={'remark': 'Multisig test'}
    )
    extrinsic = substrate.create_multisig_extrinsic(call, keypair_alice, multisig_account, era={'period': 64})
    substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)
    
    # 3. Finalize with a second signatory
    keypair_charlie = Keypair.create_from_uri('//Charlie', ss58_format=substrate.ss58_format)
    extrinsic_charlie = substrate.create_multisig_extrinsic(call, keypair_charlie, multisig_account, era={'period': 64})
    substrate.submit_extrinsic(extrinsic_charlie, wait_for_inclusion=True)
  8. Initialize the SubsquidExtension

    master

    To use the extension, first initialize a SubstrateInterface instance, then register the SubsquidExtension by providing the URL for the Giant Squid GraphQL endpoint.

    from substrateinterface import SubstrateInterface
    from substrateinterface_subsquid.extensions import SubsquidExtension
    
    substrate = SubstrateInterface(url="wss://rpc.polkadot.io")
    
    substrate.register_extension(SubsquidExtension(url='https://squid.subsquid.io/gs-explorer-polkadot/graphql'))