Extrinsics

In Portaldot, transactions are often more broadly referred to as (signed) extrinsics. The term extrinsic is generally used to mean any information that originates outside of the runtime. An extrinsic is basically a vehicle that carries the intention to execute a function call in the runtime, along with proof of the account that wants to execute it.

Creating extrinsics

Example:

call = portaldot.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5E9oDs9PjpsBbxXxRE9uMaZZhnBAV38n2ouLB28oecBDdeQo',
        'value': 1 * 10** 14
    }
)

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

try:
    receipt = portaldot.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))

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.

Extrinsic Receipts

The portaldot.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:

receipt = portaldot.submit_extrinsic(extrinsic, wait_for_inclusion=True)
print(receipt.is_success) # False
print(receipt.weight)
print(receipt.total_fee_amount)
print(receipt.error_message['name']) # 'LiquidityRestrictions'

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

receipt = portaldot.retrieve_extrinsic_by_identifier("5233297-1")

print(receipt.is_success)
print(receipt.extrinsic.call_module.name)
print(receipt.extrinsic.call.name)
print(receipt.weight)
print(receipt.total_fee_amount)
print(receipt.error_message['docs'])

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

Multisig extrinsics

Substrate has the functionality for multi-signature dispatch, allowing multiple signed origins (accounts) to coordinate and dispatch a call, derivable deterministically from the set of account IDs and the threshold number of accounts from the set that must approve it.

To initiate and finalize multisig extrinsics, the following helper functions are available:

Define the multisig account by supplying its signatories and threshold:

keypair_alice = Keypair.create_from_uri('//Alice', ss58_format=portaldot.ss58_format)

multisig_account = portaldot.generate_multisig_account(
    signatories=[
        keypair_alice.ss58_address,
        '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
        '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y'
    ],
    threshold=2
)

Then initiate the multisig extrinsic by providing the call and a keypair of one of its signatories:

call = portaldot.compose_call(
    call_module='System',
    call_function='remark_with_event',
    call_params={
        'remark': 'Multisig test'
    }
)

extrinsic = portaldot.create_multisig_extrinsic(call, keypair_alice, multisig_account, era={'period': 64})
receipt = portaldot.submit_extrinsic(extrinsic, wait_for_inclusion=True)

Then a second signatory approves and finalizes the call by providing the same call to another multisig extrinsic:

# Define the multisig account by supplying its signatories and threshold
keypair_charlie = Keypair.create_from_uri('//Charlie', ss58_format=portaldot.ss58_format)

multisig_account = portaldot.generate_multisig_account(
    signatories=[
        keypair_charlie.ss58_address,
        '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y'
    ],
    threshold=2
)

extrinsic = portaldot.create_multisig_extrinsic(call, keypair_charlie, multisig_account, era={'period': 64})
receipt = portaldot.submit_extrinsic(extrinsic, wait_for_inclusion=True)

The call will be executed when the second and final multisig extrinsic is submitted, condition and state of the multig will be checked on-chain during processing of the multisig extrinsic.

Type decomposition of call params

The structure of certain call parameters can be quite complex, then the get_param_info() function of the call function object can provide more insight how to construct those parameters:

call_function = portaldot.get_metadata_call_function("POT", "transfer")
param_info = call_function.get_param_info()

Estimate of network fees

payment_info = portaldot.get_payment_info(call=call, keypair=keypair)

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.

extrinsic = portaldot.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.