3030 GenericExtrinsic ,
3131 GenericRuntimeCallDefinition ,
3232 ss58_encode ,
33+ MultiAccountId ,
3334)
3435from websockets .asyncio .client import connect
3536from websockets .exceptions import ConnectionClosed
@@ -1137,6 +1138,32 @@ async def result_handler(
11371138 result_handler = result_handler ,
11381139 )
11391140
1141+ async def retrieve_pending_extrinsics (self ) -> list :
1142+ """
1143+ Retrieves and decodes pending extrinsics from the node's transaction pool
1144+
1145+ Returns:
1146+ list of extrinsics
1147+ """
1148+
1149+ runtime = await self .init_runtime ()
1150+
1151+ result_data = await self .rpc_request ("author_pendingExtrinsics" , [])
1152+
1153+ extrinsics = []
1154+
1155+ for extrinsic_data in result_data ["result" ]:
1156+ extrinsic = runtime .runtime_config .create_scale_object (
1157+ "Extrinsic" , metadata = runtime .metadata
1158+ )
1159+ extrinsic .decode (
1160+ ScaleBytes (extrinsic_data ),
1161+ check_remaining = self .config .get ("strict_scale_decode" ),
1162+ )
1163+ extrinsics .append (extrinsic )
1164+
1165+ return extrinsics
1166+
11401167 async def get_metadata_storage_functions (self , block_hash = None ) -> list :
11411168 """
11421169 Retrieves a list of all storage functions in metadata active at given block_hash (or chaintip if block_hash is
@@ -1802,7 +1829,7 @@ def convert_event_data(data):
18021829 events .append (convert_event_data (item ))
18031830 return events
18041831
1805- async def get_metadata (self , block_hash = None ):
1832+ async def get_metadata (self , block_hash = None ) -> MetadataV15 :
18061833 """
18071834 Returns `MetadataVersioned` object for given block_hash or chaintip if block_hash is omitted
18081835
@@ -1815,7 +1842,7 @@ async def get_metadata(self, block_hash=None):
18151842 """
18161843 runtime = await self .init_runtime (block_hash = block_hash )
18171844
1818- return runtime .metadata
1845+ return runtime .metadata_v15
18191846
18201847 @a .lru_cache (maxsize = 512 )
18211848 async def get_parent_block_hash (self , block_hash ):
@@ -1833,10 +1860,43 @@ async def _get_parent_block_hash(self, block_hash):
18331860 return block_hash
18341861 return parent_block_hash
18351862
1863+ async def get_storage_by_key (self , block_hash : str , storage_key : str ) -> Any :
1864+ """
1865+ A pass-though to existing JSONRPC method `state_getStorage`/`state_getStorageAt`
1866+
1867+ Args:
1868+ block_hash: hash of the block
1869+ storage_key: storage key to query
1870+
1871+ Returns:
1872+ result of the query
1873+
1874+ """
1875+
1876+ if await self .supports_rpc_method ("state_getStorageAt" ):
1877+ response = await self .rpc_request (
1878+ "state_getStorageAt" , [storage_key , block_hash ]
1879+ )
1880+ else :
1881+ response = await self .rpc_request (
1882+ "state_getStorage" , [storage_key , block_hash ]
1883+ )
1884+
1885+ if "result" in response :
1886+ return response .get ("result" )
1887+ elif "error" in response :
1888+ raise SubstrateRequestException (response ["error" ]["message" ])
1889+ else :
1890+ raise SubstrateRequestException (
1891+ "Unknown error occurred during retrieval of events"
1892+ )
1893+
18361894 @a .lru_cache (maxsize = 16 )
18371895 async def get_block_runtime_info (self , block_hash : str ) -> dict :
18381896 return await self ._get_block_runtime_info (block_hash )
18391897
1898+ get_block_runtime_version = get_block_runtime_info
1899+
18401900 async def _get_block_runtime_info (self , block_hash : str ) -> dict :
18411901 """
18421902 Retrieve the runtime info of given block_hash
@@ -2591,6 +2651,34 @@ async def create_signed_extrinsic(
25912651
25922652 return extrinsic
25932653
2654+ async def create_unsigned_extrinsic (self , call : GenericCall ) -> GenericExtrinsic :
2655+ """
2656+ Create unsigned extrinsic for given `Call`
2657+
2658+ Args:
2659+ call: GenericCall the call the extrinsic should contain
2660+
2661+ Returns:
2662+ GenericExtrinsic
2663+ """
2664+
2665+ runtime = await self .init_runtime ()
2666+
2667+ # Create extrinsic
2668+ extrinsic = self .runtime_config .create_scale_object (
2669+ type_string = "Extrinsic" , metadata = runtime .metadata
2670+ )
2671+
2672+ extrinsic .encode (
2673+ {
2674+ "call_function" : call .value ["call_function" ],
2675+ "call_module" : call .value ["call_module" ],
2676+ "call_args" : call .value ["call_args" ],
2677+ }
2678+ )
2679+
2680+ return extrinsic
2681+
25942682 async def get_chain_finalised_head (self ):
25952683 """
25962684 A pass-though to existing JSONRPC method `chain_getFinalizedHead`
@@ -3165,6 +3253,100 @@ async def query_map(
31653253 ignore_decoding_errors = ignore_decoding_errors ,
31663254 )
31673255
3256+ async def create_multisig_extrinsic (
3257+ self ,
3258+ call : GenericCall ,
3259+ keypair : Keypair ,
3260+ multisig_account : MultiAccountId ,
3261+ max_weight : Optional [Union [dict , int ]] = None ,
3262+ era : dict = None ,
3263+ nonce : int = None ,
3264+ tip : int = 0 ,
3265+ tip_asset_id : int = None ,
3266+ signature : Union [bytes , str ] = None ,
3267+ ) -> GenericExtrinsic :
3268+ """
3269+ Create a Multisig extrinsic that will be signed by one of the signatories. Checks on-chain if the threshold
3270+ of the multisig account is reached and try to execute the call accordingly.
3271+
3272+ Args:
3273+ call: GenericCall to create extrinsic for
3274+ keypair: Keypair of the signatory to approve given call
3275+ multisig_account: MultiAccountId to use of origin of the extrinsic (see `generate_multisig_account()`)
3276+ max_weight: Maximum allowed weight to execute the call ( Uses `get_payment_info()` by default)
3277+ era: Specify mortality in blocks in follow format: {'period': [amount_blocks]} If omitted the extrinsic is
3278+ immortal
3279+ nonce: nonce to include in extrinsics, if omitted the current nonce is retrieved on-chain
3280+ tip: The tip for the block author to gain priority during network congestion
3281+ tip_asset_id: Optional asset ID with which to pay the tip
3282+ signature: Optionally provide signature if externally signed
3283+
3284+ Returns:
3285+ GenericExtrinsic
3286+ """
3287+ if max_weight is None :
3288+ payment_info = await self .get_payment_info (call , keypair )
3289+ max_weight = payment_info ["weight" ]
3290+
3291+ # Check if call has existing approvals
3292+ multisig_details_ = await self .query (
3293+ "Multisig" , "Multisigs" , [multisig_account .value , call .call_hash ]
3294+ )
3295+ multisig_details = getattr (multisig_details_ , "value" , multisig_details_ )
3296+ if multisig_details :
3297+ maybe_timepoint = multisig_details ["when" ]
3298+ else :
3299+ maybe_timepoint = None
3300+
3301+ # Compose 'as_multi' when final, 'approve_as_multi' otherwise
3302+ if (
3303+ multisig_details .value
3304+ and len (multisig_details .value ["approvals" ]) + 1
3305+ == multisig_account .threshold
3306+ ):
3307+ multi_sig_call = await self .compose_call (
3308+ "Multisig" ,
3309+ "as_multi" ,
3310+ {
3311+ "other_signatories" : [
3312+ s
3313+ for s in multisig_account .signatories
3314+ if s != f"0x{ keypair .public_key .hex ()} "
3315+ ],
3316+ "threshold" : multisig_account .threshold ,
3317+ "maybe_timepoint" : maybe_timepoint ,
3318+ "call" : call ,
3319+ "store_call" : False ,
3320+ "max_weight" : max_weight ,
3321+ },
3322+ )
3323+ else :
3324+ multi_sig_call = await self .compose_call (
3325+ "Multisig" ,
3326+ "approve_as_multi" ,
3327+ {
3328+ "other_signatories" : [
3329+ s
3330+ for s in multisig_account .signatories
3331+ if s != f"0x{ keypair .public_key .hex ()} "
3332+ ],
3333+ "threshold" : multisig_account .threshold ,
3334+ "maybe_timepoint" : maybe_timepoint ,
3335+ "call_hash" : call .call_hash ,
3336+ "max_weight" : max_weight ,
3337+ },
3338+ )
3339+
3340+ return await self .create_signed_extrinsic (
3341+ multi_sig_call ,
3342+ keypair ,
3343+ era = era ,
3344+ nonce = nonce ,
3345+ tip = tip ,
3346+ tip_asset_id = tip_asset_id ,
3347+ signature = signature ,
3348+ )
3349+
31683350 async def submit_extrinsic (
31693351 self ,
31703352 extrinsic : GenericExtrinsic ,
0 commit comments