30
30
GenericExtrinsic ,
31
31
GenericRuntimeCallDefinition ,
32
32
ss58_encode ,
33
+ MultiAccountId ,
33
34
)
34
35
from websockets .asyncio .client import connect
35
36
from websockets .exceptions import ConnectionClosed
@@ -1137,6 +1138,32 @@ async def result_handler(
1137
1138
result_handler = result_handler ,
1138
1139
)
1139
1140
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
+
1140
1167
async def get_metadata_storage_functions (self , block_hash = None ) -> list :
1141
1168
"""
1142
1169
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):
1802
1829
events .append (convert_event_data (item ))
1803
1830
return events
1804
1831
1805
- async def get_metadata (self , block_hash = None ):
1832
+ async def get_metadata (self , block_hash = None ) -> MetadataV15 :
1806
1833
"""
1807
1834
Returns `MetadataVersioned` object for given block_hash or chaintip if block_hash is omitted
1808
1835
@@ -1815,7 +1842,7 @@ async def get_metadata(self, block_hash=None):
1815
1842
"""
1816
1843
runtime = await self .init_runtime (block_hash = block_hash )
1817
1844
1818
- return runtime .metadata
1845
+ return runtime .metadata_v15
1819
1846
1820
1847
@a .lru_cache (maxsize = 512 )
1821
1848
async def get_parent_block_hash (self , block_hash ):
@@ -1833,10 +1860,43 @@ async def _get_parent_block_hash(self, block_hash):
1833
1860
return block_hash
1834
1861
return parent_block_hash
1835
1862
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
+
1836
1894
@a .lru_cache (maxsize = 16 )
1837
1895
async def get_block_runtime_info (self , block_hash : str ) -> dict :
1838
1896
return await self ._get_block_runtime_info (block_hash )
1839
1897
1898
+ get_block_runtime_version = get_block_runtime_info
1899
+
1840
1900
async def _get_block_runtime_info (self , block_hash : str ) -> dict :
1841
1901
"""
1842
1902
Retrieve the runtime info of given block_hash
@@ -2591,6 +2651,34 @@ async def create_signed_extrinsic(
2591
2651
2592
2652
return extrinsic
2593
2653
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
+
2594
2682
async def get_chain_finalised_head (self ):
2595
2683
"""
2596
2684
A pass-though to existing JSONRPC method `chain_getFinalizedHead`
@@ -3165,6 +3253,100 @@ async def query_map(
3165
3253
ignore_decoding_errors = ignore_decoding_errors ,
3166
3254
)
3167
3255
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
+
3168
3350
async def submit_extrinsic (
3169
3351
self ,
3170
3352
extrinsic : GenericExtrinsic ,
0 commit comments