diff --git a/async_substrate_interface/async_substrate.py b/async_substrate_interface/async_substrate.py index c2342e8..6ff837b 100644 --- a/async_substrate_interface/async_substrate.py +++ b/async_substrate_interface/async_substrate.py @@ -3500,7 +3500,13 @@ async def get_constant( return None async def get_payment_info( - self, call: GenericCall, keypair: Keypair + self, + call: GenericCall, + keypair: Keypair, + era: Optional[Union[dict, str]] = None, + nonce: Optional[int] = None, + tip: int = 0, + tip_asset_id: Optional[int] = None, ) -> dict[str, Any]: """ Retrieves fee estimation via RPC for given extrinsic @@ -3509,6 +3515,11 @@ async def get_payment_info( call: Call object to estimate fees for keypair: Keypair of the sender, does not have to include private key because no valid signature is required + era: Specify mortality in blocks in follow format: + {'period': [amount_blocks]} If omitted the extrinsic is immortal + nonce: nonce to include in extrinsics, if omitted the current nonce is retrieved on-chain + tip: The tip for the block author to gain priority during network congestion + tip_asset_id: Optional asset ID with which to pay the tip Returns: Dict with payment info @@ -3528,7 +3539,13 @@ async def get_payment_info( # Create extrinsic extrinsic = await self.create_signed_extrinsic( - call=call, keypair=keypair, signature=signature + call=call, + keypair=keypair, + era=era, + nonce=nonce, + tip=tip, + tip_asset_id=tip_asset_id, + signature=signature, ) extrinsic_len = len(extrinsic.data) diff --git a/async_substrate_interface/sync_substrate.py b/async_substrate_interface/sync_substrate.py index 373e8cd..7f233eb 100644 --- a/async_substrate_interface/sync_substrate.py +++ b/async_substrate_interface/sync_substrate.py @@ -2779,7 +2779,15 @@ def get_constant( else: return None - def get_payment_info(self, call: GenericCall, keypair: Keypair) -> dict[str, Any]: + def get_payment_info( + self, + call: GenericCall, + keypair: Keypair, + era: Optional[Union[dict, str]] = None, + nonce: Optional[int] = None, + tip: int = 0, + tip_asset_id: Optional[int] = None, + ) -> dict[str, Any]: """ Retrieves fee estimation via RPC for given extrinsic @@ -2787,6 +2795,11 @@ def get_payment_info(self, call: GenericCall, keypair: Keypair) -> dict[str, Any call: Call object to estimate fees for keypair: Keypair of the sender, does not have to include private key because no valid signature is required + era: Specify mortality in blocks in follow format: + {'period': [amount_blocks]} If omitted the extrinsic is immortal + nonce: nonce to include in extrinsics, if omitted the current nonce is retrieved on-chain + tip: The tip for the block author to gain priority during network congestion + tip_asset_id: Optional asset ID with which to pay the tip Returns: Dict with payment info @@ -2806,7 +2819,13 @@ def get_payment_info(self, call: GenericCall, keypair: Keypair) -> dict[str, Any # Create extrinsic extrinsic = self.create_signed_extrinsic( - call=call, keypair=keypair, signature=signature + call=call, + keypair=keypair, + era=era, + nonce=nonce, + tip=tip, + tip_asset_id=tip_asset_id, + signature=signature, ) extrinsic_len = len(extrinsic.data) diff --git a/pyproject.toml b/pyproject.toml index ddb2b89..3bf6bc5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,5 +56,6 @@ dev = [ "pytest-split==0.10.0", "pytest-xdist==3.6.1", "pytest-rerunfailures==10.2", - "substrate-interface" + "substrate-interface", + "bittensor-wallet>=4.0.0" ] diff --git a/tests/integration_tests/test_async_substrate_interface.py b/tests/integration_tests/test_async_substrate_interface.py index 8957314..1acbed6 100644 --- a/tests/integration_tests/test_async_substrate_interface.py +++ b/tests/integration_tests/test_async_substrate_interface.py @@ -4,6 +4,7 @@ import time import threading +import bittensor_wallet import pytest from scalecodec import ss58_encode @@ -235,3 +236,42 @@ async def test_improved_reconnection(): shutdown_thread.start() shutdown_thread.join(timeout=5) server_thread.join(timeout=5) + + +@pytest.mark.asyncio +async def test_get_payment_info(): + alice_coldkey = bittensor_wallet.Keypair.create_from_uri("//Alice") + bob_coldkey = bittensor_wallet.Keypair.create_from_uri("//Bob") + async with AsyncSubstrateInterface( + LATENT_LITE_ENTRYPOINT, ss58_format=42, chain_name="Bittensor" + ) as substrate: + block_hash = await substrate.get_chain_head() + call = await substrate.compose_call( + "Balances", + "transfer_keep_alive", + {"dest": bob_coldkey.ss58_address, "value": 100_000}, + block_hash, + ) + payment_info = await substrate.get_payment_info( + call=call, + keypair=alice_coldkey, + ) + partial_fee_no_era = payment_info["partial_fee"] + assert partial_fee_no_era > 0 + payment_info_era = await substrate.get_payment_info( + call=call, keypair=alice_coldkey, era={"period": 64} + ) + partial_fee_era = payment_info_era["partial_fee"] + assert partial_fee_era > partial_fee_no_era + + payment_info_all_options = await substrate.get_payment_info( + call=call, + keypair=alice_coldkey, + era={"period": 64}, + nonce=await substrate.get_account_nonce(alice_coldkey.ss58_address), + tip=5_000_000, + tip_asset_id=64, + ) + partial_fee_all_options = payment_info_all_options["partial_fee"] + assert partial_fee_all_options > partial_fee_no_era + assert partial_fee_all_options > partial_fee_era diff --git a/tests/integration_tests/test_substrate_interface.py b/tests/integration_tests/test_substrate_interface.py index ad80401..42885ac 100644 --- a/tests/integration_tests/test_substrate_interface.py +++ b/tests/integration_tests/test_substrate_interface.py @@ -1,3 +1,4 @@ +import bittensor_wallet from scalecodec import ss58_encode from async_substrate_interface.sync_substrate import SubstrateInterface @@ -112,3 +113,41 @@ def test_query_map_with_odd_number_of_params(): first_record = qm.records[0] assert len(first_record) == 2 assert len(first_record[0]) == 4 + + +def test_get_payment_info(): + alice_coldkey = bittensor_wallet.Keypair.create_from_uri("//Alice") + bob_coldkey = bittensor_wallet.Keypair.create_from_uri("//Bob") + with SubstrateInterface( + LATENT_LITE_ENTRYPOINT, ss58_format=42, chain_name="Bittensor" + ) as substrate: + block_hash = substrate.get_chain_head() + call = substrate.compose_call( + "Balances", + "transfer_keep_alive", + {"dest": bob_coldkey.ss58_address, "value": 100_000}, + block_hash, + ) + payment_info = substrate.get_payment_info( + call=call, + keypair=alice_coldkey, + ) + partial_fee_no_era = payment_info["partial_fee"] + assert partial_fee_no_era > 0 + payment_info_era = substrate.get_payment_info( + call=call, keypair=alice_coldkey, era={"period": 64} + ) + partial_fee_era = payment_info_era["partial_fee"] + assert partial_fee_era > partial_fee_no_era + + payment_info_all_options = substrate.get_payment_info( + call=call, + keypair=alice_coldkey, + era={"period": 64}, + nonce=substrate.get_account_nonce(alice_coldkey.ss58_address), + tip=5_000_000, + tip_asset_id=64, + ) + partial_fee_all_options = payment_info_all_options["partial_fee"] + assert partial_fee_all_options > partial_fee_no_era + assert partial_fee_all_options > partial_fee_era