From d0b38625202ab7988cfe8aca6829e0f7132bf09d Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 20 Nov 2025 20:14:08 -0800 Subject: [PATCH 01/67] add `MevShield` pallet function call builder class --- bittensor/core/extrinsics/pallets/__init__.py | 2 + .../core/extrinsics/pallets/mev_shield.py | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 bittensor/core/extrinsics/pallets/mev_shield.py diff --git a/bittensor/core/extrinsics/pallets/__init__.py b/bittensor/core/extrinsics/pallets/__init__.py index facf04b2b8..328eb67c83 100644 --- a/bittensor/core/extrinsics/pallets/__init__.py +++ b/bittensor/core/extrinsics/pallets/__init__.py @@ -2,6 +2,7 @@ from .balances import Balances from .commitments import Commitments from .crowdloan import Crowdloan +from .mev_shield import MevShield from .proxy import Proxy from .subtensor_module import SubtensorModule from .sudo import Sudo @@ -13,6 +14,7 @@ "Balances", "Commitments", "Crowdloan", + "MevShield", "Proxy", "SubtensorModule", "Sudo", diff --git a/bittensor/core/extrinsics/pallets/mev_shield.py b/bittensor/core/extrinsics/pallets/mev_shield.py new file mode 100644 index 0000000000..cef4995bc8 --- /dev/null +++ b/bittensor/core/extrinsics/pallets/mev_shield.py @@ -0,0 +1,59 @@ +from dataclasses import dataclass + +from .base import CallBuilder as _BasePallet, Call + + +@dataclass +class MevShield(_BasePallet): + """Factory class for creating GenericCall objects for MevShield pallet functions. + + This class provides methods to create GenericCall instances for all MevShield pallet extrinsics. + + Works with both sync (Subtensor) and async (AsyncSubtensor) instances. For async operations, pass an AsyncSubtensor + instance and await the result. + + Example: + # Sync usage + call = MevShield(subtensor).submit_encrypted( + commitment="0x1234...", + ciphertext=b"encrypted_data..." + ) + response = subtensor.sign_and_send_extrinsic(call=call, ...) + + # Async usage + call = await MevShield(async_subtensor).submit_encrypted( + commitment="0x1234...", + ciphertext=b"encrypted_data..." + ) + response = await async_subtensor.sign_and_send_extrinsic(call=call, ...) + """ + + def submit_encrypted( + self, + commitment: str, + ciphertext: bytes, + ) -> Call: + """Returns GenericCall instance for MevShield function submit_encrypted. + + This function submits an encrypted extrinsic to the MEV Shield pallet. The extrinsic remains encrypted in the + transaction pool until it is included in a block and decrypted by validators. + + Parameters: + commitment: The blake2_256 hash of the payload_core (signer + nonce + SCALE(call)). Must be a hex string + with "0x" prefix. + ciphertext: The encrypted blob containing the payload and signature. + Format: [u16 kem_len LE][kem_ct][nonce24][aead_ct] + Maximum size: 8192 bytes. + + Returns: + GenericCall instance ready for extrinsic submission. + + Note: + The commitment is used to verify the ciphertext's content at decryption time. The ciphertext is encrypted + using ML-KEM-768 + XChaCha20Poly1305 with the public key from the NextKey storage item, which rotates every + block. + """ + return self.create_composed_call( + commitment=commitment, + ciphertext=ciphertext, + ) From 4fad186d13171537163cc283dd437ee9a064be7b Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Fri, 21 Nov 2025 15:43:18 -0800 Subject: [PATCH 02/67] update SubtensorApi --- bittensor/extras/subtensor_api/__init__.py | 6 ++++++ bittensor/extras/subtensor_api/mev_shield.py | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 bittensor/extras/subtensor_api/mev_shield.py diff --git a/bittensor/extras/subtensor_api/__init__.py b/bittensor/extras/subtensor_api/__init__.py index ee001ebc63..7f538e2ed9 100644 --- a/bittensor/extras/subtensor_api/__init__.py +++ b/bittensor/extras/subtensor_api/__init__.py @@ -8,6 +8,7 @@ from .delegates import Delegates as _Delegates from .extrinsics import Extrinsics as _Extrinsics from .metagraphs import Metagraphs as _Metagraphs +from .mev_shield import MevShield as _MevShield from .neurons import Neurons as _Neurons from .proxy import Proxy as _Proxy from .queries import Queries as _Queries @@ -230,6 +231,11 @@ def metagraphs(self): """Property to access metagraphs methods.""" return _Metagraphs(self.inner_subtensor) + @property + def mev_shield(self): + """Property to access MEV Shield methods.""" + return _MevShield(self.inner_subtensor) + @property def neurons(self): """Property to access neurons methods.""" diff --git a/bittensor/extras/subtensor_api/mev_shield.py b/bittensor/extras/subtensor_api/mev_shield.py new file mode 100644 index 0000000000..eb778ce0c8 --- /dev/null +++ b/bittensor/extras/subtensor_api/mev_shield.py @@ -0,0 +1,20 @@ +from typing import Union + +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor +from bittensor.core.subtensor import Subtensor as _Subtensor + + +class MevShield: + """Class for managing MEV Shield operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + # Storage queries + self.get_mev_shield_current_key = subtensor.get_mev_shield_current_key + self.get_mev_shield_epoch = subtensor.get_mev_shield_epoch + self.get_mev_shield_next_key = subtensor.get_mev_shield_next_key + self.get_mev_shield_submission = subtensor.get_mev_shield_submission + + # Extrinsics + self.mev_announce_next_key = subtensor.mev_announce_next_key + self.mev_execute_revealed = subtensor.mev_execute_revealed + self.mev_submit_encrypted = subtensor.mev_submit_encrypted From 89245b0e4bb416c09993ef66c05155bc4971833d Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Fri, 21 Nov 2025 15:43:34 -0800 Subject: [PATCH 03/67] update pallet functions call builder --- .../core/extrinsics/pallets/mev_shield.py | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/bittensor/core/extrinsics/pallets/mev_shield.py b/bittensor/core/extrinsics/pallets/mev_shield.py index cef4995bc8..3996914d87 100644 --- a/bittensor/core/extrinsics/pallets/mev_shield.py +++ b/bittensor/core/extrinsics/pallets/mev_shield.py @@ -1,5 +1,7 @@ from dataclasses import dataclass +from scalecodec import GenericCall + from .base import CallBuilder as _BasePallet, Call @@ -28,6 +30,70 @@ class MevShield(_BasePallet): response = await async_subtensor.sign_and_send_extrinsic(call=call, ...) """ + def announce_next_key( + self, + public_key: bytes, + epoch: int, + ) -> Call: + """Returns GenericCall instance for MevShield function announce_next_key. + + This function allows Aura validators to announce the next ML-KEM-768 public key that will be used for encryption + in the next block. The key will be rotated from NextKey to CurrentKey at the beginning of the next block. + + Parameters: + public_key: The ML-KEM-768 public key as bytes. Must be exactly 1184 bytes for ML-KEM-768. + epoch: The epoch number associated with this key. + + Returns: + GenericCall instance ready for extrinsic submission. + + Note: + Only current Aura validators can call this function. The public_key length must be exactly 1184 bytes + (MAX_KYBER768_PK_LENGTH). This is enforced by the pallet. + """ + return self.create_composed_call( + public_key=public_key, + epoch=epoch, + ) + + def execute_revealed( + self, + id: str, + signer: str, + nonce: int, + call: "GenericCall", + signature: bytes, + ) -> Call: + """Returns GenericCall instance for MevShield function execute_revealed. + + This function is executed by the block author (validator) to execute a decrypted extrinsic. It verifies the + commitment, signature, and nonce before dispatching the inner call. + + Parameters: + id: The submission ID (hash of signer + commitment + ciphertext). Must be a hex string with "0x" prefix. + signer: The SS58 address of the account that originally submitted the encrypted extrinsic. + nonce: The nonce that was used when creating the payload_core. + call: The GenericCall object to execute (the decrypted call). + signature: The signature bytes (MultiSignature format) that was included in the encrypted payload. + + Returns: + GenericCall instance ready for extrinsic submission. + + Note: + This is an unsigned extrinsic that can only be called by the block author. It verifies: + 1. Commitment matches the stored submission + 2. Signature is valid over "mev-shield:v1" + genesis_hash + payload_core + 3. Nonce matches the current account nonce + 4. Then dispatches the inner call from the signer + """ + return self.create_composed_call( + id=id, + signer=signer, + nonce=nonce, + call=call, + signature=signature, + ) + def submit_encrypted( self, commitment: str, From 91c70f1e9f857497a758a9b5f24be9dba0a82f4b Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Fri, 21 Nov 2025 17:33:13 -0800 Subject: [PATCH 04/67] add e2e tests --- tests/e2e_tests/test_mev_shield.py | 573 +++++++++++++++++++++++++++++ 1 file changed, 573 insertions(+) create mode 100644 tests/e2e_tests/test_mev_shield.py diff --git a/tests/e2e_tests/test_mev_shield.py b/tests/e2e_tests/test_mev_shield.py new file mode 100644 index 0000000000..4ede73df8d --- /dev/null +++ b/tests/e2e_tests/test_mev_shield.py @@ -0,0 +1,573 @@ +"""E2E tests for MEV Shield functionality.""" + +import hashlib + +from bittensor_drand import generate_mlkem768_keypair +from bittensor_wallet import Wallet + +from bittensor.core.extrinsics.mev_shield import ( + announce_next_key_extrinsic, + submit_encrypted_extrinsic, +) +from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils.balance import Balance +from bittensor.utils.btlogging import logging +from tests.e2e_tests.utils import ( + AdminUtils, + NETUID, + SUDO_SET_TEMPO, + TestSubnet, +) + + +def add_balance_to_wallet_hk(subtensor, wallet: "Wallet", tao_amount: int) -> ExtrinsicResponse: + """Adds 100 TAO to Alice's HK balance.""" + return subtensor.extrinsics.transfer( + wallet=wallet, + destination_ss58=wallet.hotkeypub.ss58_address, + amount=Balance.from_tao(tao_amount) + ) + + +def test_mev_shield_storage_queries(subtensor): + """Tests querying MevShield storage items. + + Steps: + 1. Query CurrentKey (may be None) + 2. Query NextKey (may be None) + 3. Query Epoch (always >= 0) + 4. Query Submissions (all, returns dict) + """ + + # Query Epoch - should always be available + epoch = subtensor.mev_shield.get_mev_shield_epoch() + assert isinstance(epoch, int), f"Epoch should be int, got {type(epoch)}" + assert epoch >= 0, f"Epoch should be non-negative, got {epoch}" + + # Query CurrentKey - may be None if no key announced yet + current_key = subtensor.mev_shield.get_mev_shield_current_key() + if current_key is not None: + public_key_bytes, epoch_val = current_key + assert isinstance(public_key_bytes, bytes), "Public key should be bytes" + assert len(public_key_bytes) == 1184, ( + f"ML-KEM-768 public key should be 1184 bytes, got {len(public_key_bytes)}" + ) + assert isinstance(epoch_val, int), "Epoch should be int" + logging.debug( + f"CurrentKey found: epoch={epoch_val}, key_size={len(public_key_bytes)}" + ) + + # Query NextKey - may be None if no key announced yet + next_key = subtensor.mev_shield.get_mev_shield_next_key() + if next_key is not None: + public_key_bytes, epoch_val = next_key + assert isinstance(public_key_bytes, bytes), "Public key should be bytes" + assert len(public_key_bytes) == 1184, ( + f"ML-KEM-768 public key should be 1184 bytes, got {len(public_key_bytes)}" + ) + assert isinstance(epoch_val, int), "Epoch should be int" + logging.debug( + f"NextKey found: epoch={epoch_val}, key_size={len(public_key_bytes)}" + ) + + # Query all submissions - should always return a dict + all_submissions = subtensor.mev_shield.get_mev_shield_submission() + assert isinstance(all_submissions, dict), ( + f"Should return dict of all submissions, got {type(all_submissions)}" + ) + logging.debug(f"Found {len(all_submissions)} submission(s) in storage") + + +def test_mev_shield_announce_next_key(subtensor, alice_wallet): + """Tests announcing next key as a validator. + + Steps: + 1. Generate ML-KEM-768 keypair using bittensor_drand + 2. Get current epoch + 3. Announce next key with next epoch + 4. Wait for next block + 5. Verify NextKey is set correctly + 6. Verify public_key size is 1184 bytes + 7. Verify epoch matches + """ + # add founds to alice HK + response = add_balance_to_wallet_hk(subtensor, alice_wallet, 100) + assert response.success, response.message + + # Set tempo to 100 blocks for root network (netuid=0) to ensure stable epoch timing + # MEV Shield epoch depends on root network tempo, so we need predictable epoch boundaries + TEMPO_TO_SET = 100 + root_sn = TestSubnet(subtensor, netuid=0) + tempo_response = root_sn.execute_one( + SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, 0, TEMPO_TO_SET) + ) + assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" + + # Wait a few blocks after setting tempo to ensure state is synchronized + # This prevents "Transaction is outdated" errors by allowing the chain to process + # the tempo change and stabilize before we submit the announce transaction. + current_block = subtensor.block + subtensor.wait_for_block(current_block + 5) + + # Generate ML-KEM-768 keypair using bittensor_drand + public_key_bytes = generate_mlkem768_keypair() + assert len(public_key_bytes) == 1184, ( + f"Generated key should be 1184 bytes, got {len(public_key_bytes)}" + ) + + # Get current epoch and prepare next epoch + current_epoch = subtensor.mev_shield.get_mev_shield_epoch() + next_epoch = current_epoch + 1 + + logging.info( + f"Announcing next key for epoch {next_epoch} (current epoch: {current_epoch})" + ) + + # Announce next key with explicit period to avoid transaction expiration + # Use a longer period (256 blocks) to ensure transaction doesn't expire during epoch transitions + response = announce_next_key_extrinsic( + subtensor=subtensor, + wallet=alice_wallet, + public_key=public_key_bytes, + epoch=next_epoch, + period=256, # Longer period to avoid "Transaction is outdated" errors + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert response.success, f"Failed to announce next key: {response.message}" + + # Verify that extrinsic was actually executed successfully (not just included) + assert response.extrinsic_receipt is not None, ( + "Extrinsic receipt should be available" + ) + assert response.extrinsic_receipt.is_success, ( + f"Extrinsic execution failed: {response.extrinsic_receipt.error_message}" + ) + + logging.info("Next key announced successfully, waiting for block...") + + # Wait for next block to ensure the announcement is processed + current_block = subtensor.block + subtensor.wait_for_block(current_block + 1) + + # Verify NextKey is set + next_key_result = subtensor.mev_shield.get_mev_shield_next_key() + assert next_key_result is not None, "NextKey should be set after announcement" + + pk_bytes, epoch = next_key_result + assert pk_bytes == public_key_bytes, "Public key should match announced key" + assert len(pk_bytes) == 1184, ( + f"Public key size should be 1184 bytes, got {len(pk_bytes)}" + ) + assert epoch == next_epoch, ( + f"Epoch should match announced epoch {next_epoch}, got {epoch}" + ) + + logging.info(f"NextKey verified: epoch={epoch}, key_size={len(pk_bytes)}") + + +def test_mev_shield_submit_encrypted_full_flow(subtensor, bob_wallet, alice_wallet): + """Tests submitting an encrypted extrinsic with full verification. + + Steps: + 1. Ensure NextKey exists (announce if needed) + 2. Create a simple transfer call + 3. Submit encrypted extrinsic + 4. Wait for inclusion + 5. Verify submission exists in storage + 6. Verify commitment matches computed value + 7. Verify author matches wallet address + 8. Verify submitted_in block number + """ + + # add founds to alice, bob HK + for w in [alice_wallet, bob_wallet]: + response = add_balance_to_wallet_hk(subtensor, w, 100) + assert response.success, response.message + + # Ensure NextKey exists - if not, we need to announce it first using alice_wallet (validator) + next_key_result = subtensor.mev_shield.get_mev_shield_next_key() + if next_key_result is None: + # Set tempo to 100 blocks for root network (netuid=0) to ensure stable epoch timing + # MEV Shield epoch depends on root network tempo, so we need predictable epoch boundaries + TEMPO_TO_SET = 100 + root_sn = TestSubnet(subtensor, netuid=0) + tempo_response = root_sn.execute_one( + SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, 0, TEMPO_TO_SET) + ) + assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" + + # Wait for first block of next epoch to avoid "Transaction is outdated" error + # MEV Shield epoch validation requires that the announced epoch matches the current epoch + # when the transaction is processed. By waiting for the start of the next epoch, we ensure + # that our announced epoch (current_epoch + 1) will be valid when the transaction executes. + next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(netuid=0) + if next_epoch_start_block: + logging.info(f"Waiting for next epoch start block: {next_epoch_start_block}") + subtensor.wait_for_block(next_epoch_start_block) + # Wait one more block to ensure all state is synchronized + current_block = subtensor.block + subtensor.wait_for_block(current_block + 1) + + # Generate and announce a key first using alice_wallet (which is a validator in localnet) + public_key_bytes = generate_mlkem768_keypair() + current_epoch = subtensor.mev_shield.get_mev_shield_epoch() + next_epoch = current_epoch + 1 + + logging.info( + f"NextKey not found, announcing key for epoch {next_epoch} using Alice wallet (validator)" + ) + + # Announce key using validator wallet with explicit period to avoid transaction expiration + # Use a longer period (256 blocks) to ensure transaction doesn't expire during epoch transitions + announce_response = announce_next_key_extrinsic( + subtensor=subtensor, + wallet=alice_wallet, + public_key=public_key_bytes, + epoch=next_epoch, + period=256, # Longer period to avoid "Transaction is outdated" errors + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert announce_response.success, ( + f"Failed to announce next key: {announce_response.message}" + ) + assert announce_response.extrinsic_receipt is not None, ( + "Extrinsic receipt should be available" + ) + assert announce_response.extrinsic_receipt.is_success, ( + f"Extrinsic execution failed: {announce_response.extrinsic_receipt.error_message}" + ) + + # Wait for block to ensure key is set + current_block = subtensor.block + subtensor.wait_for_block(current_block + 1) + + # Verify NextKey is now set + next_key_result = subtensor.mev_shield.get_mev_shield_next_key() + assert next_key_result is not None, "NextKey should be set after announcement" + + pk_bytes, epoch = next_key_result + logging.info(f"Using NextKey with epoch {epoch} for encryption") + + # Create a simple transfer call + transfer_call = SubtensorModule(subtensor).transfer( + dest=bob_wallet.coldkey.ss58_address, + value=1000000, # 1 TAO in RAO + ) + + # Get nonce for commitment calculation + nonce = subtensor.substrate.get_account_next_index(bob_wallet.coldkey.ss58_address) + signer_bytes = bob_wallet.coldkey.public_key + nonce_bytes = nonce.to_bytes(4, "little") + scale_call_bytes = transfer_call.encode() + payload_core = signer_bytes + nonce_bytes + scale_call_bytes + + # Compute expected commitment (blake2_256 = blake2b with digest_size=32) + expected_commitment_hash = hashlib.blake2b(payload_core, digest_size=32).digest() + expected_commitment_hex = "0x" + expected_commitment_hash.hex() + + logging.info( + f"Submitting encrypted extrinsic with expected commitment: {expected_commitment_hex}" + ) + + # Submit encrypted extrinsic + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=bob_wallet, + call=transfer_call, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert response.success, f"Failed to submit encrypted extrinsic: {response.message}" + + logging.info("Encrypted extrinsic submitted successfully, waiting for block...") + + # Wait for block to be finalized + current_block = subtensor.block + subtensor.wait_for_block(current_block + 1) + + # Find submission by commitment + all_submissions = subtensor.mev_shield.get_mev_shield_submission() + submission = None + for sub_id, sub_data in all_submissions.items(): + if sub_data["commitment"] == expected_commitment_hex: + submission = sub_data + break + + assert submission is not None, ( + f"Submission not found in storage. Expected commitment: {expected_commitment_hex}" + ) + + assert submission["commitment"] == expected_commitment_hex, ( + f"Commitment mismatch: expected {expected_commitment_hex}, got {submission['commitment']}" + ) + assert submission["author"] == bob_wallet.coldkey.ss58_address, ( + f"Author mismatch: expected {bob_wallet.coldkey.ss58_address}, got {submission['author']}" + ) + assert isinstance(submission["submitted_in"], int), "submitted_in should be int" + assert submission["submitted_in"] > 0, "submitted_in should be positive" + assert isinstance(submission["ciphertext"], bytes), "ciphertext should be bytes" + assert len(submission["ciphertext"]) > 0, "ciphertext should not be empty" + + logging.info( + f"Submission verified: commitment={submission['commitment']}, " + f"author={submission['author']}, submitted_in={submission['submitted_in']}" + ) + + +def test_mev_shield_key_rotation(subtensor, alice_wallet): + """Tests key rotation from NextKey to CurrentKey. + + Steps: + 1. Generate ML-KEM-768 keypair + 2. Announce NextKey + 3. Verify NextKey is set + 4. Wait for next block (triggers rotation) + 5. Verify CurrentKey now contains the old NextKey + 6. Verify Epoch has been updated + """ + # add founds to alice HK + response = add_balance_to_wallet_hk(subtensor, alice_wallet, 100) + assert response.success, response.message + + # Set tempo to 100 blocks for root network (netuid=0) to ensure stable epoch timing + # MEV Shield epoch depends on root network tempo, so we need predictable epoch boundaries + TEMPO_TO_SET = 100 + root_sn = TestSubnet(subtensor, netuid=0) + tempo_response = root_sn.execute_one( + SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, 0, TEMPO_TO_SET) + ) + assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" + + # Wait a few blocks after setting tempo to ensure state is synchronized + # This prevents "Transaction is outdated" errors by allowing the chain to process + # the tempo change and stabilize before we submit the announce transaction. + current_block = subtensor.block + subtensor.wait_for_block(current_block + 5) + + # Generate ML-KEM-768 keypair using bittensor_drand + public_key_bytes = generate_mlkem768_keypair() + + # Get current epoch + current_epoch = subtensor.mev_shield.get_mev_shield_epoch() + next_epoch = current_epoch + 1 + + logging.info(f"Testing key rotation: announcing NextKey for epoch {next_epoch}") + + # Announce NextKey with explicit period to avoid transaction expiration + # Use a longer period (256 blocks) to ensure transaction doesn't expire during epoch transitions + response = announce_next_key_extrinsic( + subtensor=subtensor, + wallet=alice_wallet, + public_key=public_key_bytes, + epoch=next_epoch, + period=256, # Longer period to avoid "Transaction is outdated" errors + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert response.success, f"Failed to announce next key: {response.message}" + + # Verify that extrinsic was actually executed successfully (not just included) + assert response.extrinsic_receipt is not None, ( + "Extrinsic receipt should be available" + ) + assert response.extrinsic_receipt.is_success, ( + f"Extrinsic execution failed: {response.extrinsic_receipt.error_message}" + ) + + # Verify NextKey is set + next_key_before = subtensor.mev_shield.get_mev_shield_next_key() + assert next_key_before is not None, "NextKey should be set after announcement" + pk_bytes_before, epoch_before = next_key_before + assert pk_bytes_before == public_key_bytes, ( + "NextKey public key should match announced key" + ) + assert epoch_before == next_epoch, ( + f"NextKey epoch should be {next_epoch}, got {epoch_before}" + ) + + logging.info(f"NextKey set: epoch={epoch_before}, waiting for rotation...") + + # Wait for next block (this triggers rotation: NextKey -> CurrentKey) + current_block = subtensor.block + subtensor.wait_for_block(current_block + 1) + + # Verify CurrentKey now contains the old NextKey + current_key_after = subtensor.mev_shield.get_mev_shield_current_key() + assert current_key_after is not None, "CurrentKey should be set after rotation" + + pk_bytes_after, epoch_after = current_key_after + assert pk_bytes_after == public_key_bytes, ( + "CurrentKey should contain the old NextKey" + ) + assert epoch_after == next_epoch, ( + f"CurrentKey epoch should be {next_epoch}, got {epoch_after}" + ) + + # Verify Epoch has been updated + new_epoch = subtensor.mev_shield.get_mev_shield_epoch() + assert new_epoch == next_epoch, ( + f"Epoch should be updated to {next_epoch}, got {new_epoch}" + ) + + logging.info( + f"Key rotation verified: CurrentKey epoch={epoch_after}, global epoch={new_epoch}" + ) + + +def test_mev_shield_commitment_verification(subtensor, bob_wallet, alice_wallet): + """Tests commitment verification by recomputing it from payload. + + Steps: + 1. Ensure NextKey exists + 2. Create a call + 3. Submit encrypted extrinsic + 4. Get submission from storage + 5. Recompute payload_core from call parameters + 6. Recompute commitment (blake2b) + 7. Verify commitment from storage matches recomputed value + """ + + # add founds to alice, bob HK + for w in [alice_wallet, bob_wallet]: + response = add_balance_to_wallet_hk(subtensor, w, 100) + assert response.success, response.message + + # Ensure NextKey exists - if not, we need to announce it first using alice_wallet (validator) + next_key_result = subtensor.mev_shield.get_mev_shield_next_key() + if next_key_result is None: + # Set tempo to 100 blocks for root network (netuid=0) to ensure stable epoch timing + # MEV Shield epoch depends on root network tempo, so we need predictable epoch boundaries + TEMPO_TO_SET = 100 + root_sn = TestSubnet(subtensor, netuid=0) + tempo_response = root_sn.execute_one( + SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, 0, TEMPO_TO_SET) + ) + assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" + + # Wait for first block of next epoch to avoid "Transaction is outdated" error + # MEV Shield epoch validation requires that the announced epoch matches the current epoch + # when the transaction is processed. By waiting for the start of the next epoch, we ensure + # that our announced epoch (current_epoch + 1) will be valid when the transaction executes. + next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(netuid=0) + if next_epoch_start_block: + logging.info(f"Waiting for next epoch start block: {next_epoch_start_block}") + subtensor.wait_for_block(next_epoch_start_block) + # Wait one more block to ensure all state is synchronized + current_block = subtensor.block + subtensor.wait_for_block(current_block + 1) + + # Generate and announce a key first using alice_wallet (which is a validator in localnet) + public_key_bytes = generate_mlkem768_keypair() + current_epoch = subtensor.mev_shield.get_mev_shield_epoch() + next_epoch = current_epoch + 1 + + logging.info( + f"NextKey not found, announcing key for epoch {next_epoch} using Alice wallet (validator)" + ) + + # Announce key using validator wallet with explicit period to avoid transaction expiration + # Use a longer period (256 blocks) to ensure transaction doesn't expire during epoch transitions + announce_response = announce_next_key_extrinsic( + subtensor=subtensor, + wallet=alice_wallet, + public_key=public_key_bytes, + epoch=next_epoch, + period=256, # Longer period to avoid "Transaction is outdated" errors + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert announce_response.success, ( + f"Failed to announce next key: {announce_response.message}" + ) + assert announce_response.extrinsic_receipt is not None, ( + "Extrinsic receipt should be available" + ) + assert announce_response.extrinsic_receipt.is_success, ( + f"Extrinsic execution failed: {announce_response.extrinsic_receipt.error_message}" + ) + + # Wait for block to ensure key is set + current_block = subtensor.block + subtensor.wait_for_block(current_block + 1) + + # Verify NextKey is now set + next_key_result = subtensor.mev_shield.get_mev_shield_next_key() + assert next_key_result is not None, "NextKey should be set after announcement" + + # Create a simple transfer call + transfer_call = SubtensorModule(subtensor).transfer( + dest=bob_wallet.coldkey.ss58_address, + value=2000000, # 2 TAO in RAO + ) + + # Get nonce that will be used + nonce = subtensor.substrate.get_account_next_index(bob_wallet.coldkey.ss58_address) + + # Compute payload_core: signer (32B) + nonce (u32 LE, 4B) + SCALE(call) + signer_bytes = bob_wallet.coldkey.public_key + nonce_bytes = nonce.to_bytes(4, "little") + scale_call_bytes = transfer_call.encode() + payload_core = signer_bytes + nonce_bytes + scale_call_bytes + + # Compute expected commitment (blake2_256 = blake2b with digest_size=32) + expected_commitment_hash = hashlib.blake2b(payload_core, digest_size=32).digest() + expected_commitment_hex = "0x" + expected_commitment_hash.hex() + + logging.info( + f"Submitting encrypted extrinsic, expected commitment: {expected_commitment_hex}" + ) + + # Submit encrypted extrinsic + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=bob_wallet, + call=transfer_call, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert response.success, f"Failed to submit encrypted extrinsic: {response.message}" + + # Wait for block + current_block = subtensor.block + subtensor.wait_for_block(current_block + 1) + + # Find submission by commitment + all_submissions = subtensor.mev_shield.get_mev_shield_submission() + submission = None + for sub_id, sub_data in all_submissions.items(): + if sub_data["commitment"] == expected_commitment_hex: + submission = sub_data + break + + assert submission is not None, ( + f"Submission not found with commitment {expected_commitment_hex}" + ) + + # Verify commitment matches + storage_commitment = submission["commitment"] + assert storage_commitment == expected_commitment_hex, ( + f"Commitment mismatch: storage has {storage_commitment}, " + f"expected {expected_commitment_hex}" + ) + + # Recompute commitment to double-check + recomputed_commitment_hash = hashlib.blake2b(payload_core, digest_size=32).digest() + recomputed_commitment_hex = "0x" + recomputed_commitment_hash.hex() + + assert recomputed_commitment_hex == expected_commitment_hex, ( + "Recomputed commitment should match expected" + ) + assert storage_commitment == recomputed_commitment_hex, ( + f"Storage commitment {storage_commitment} should match recomputed {recomputed_commitment_hex}" + ) + + logging.info(f"Commitment verification passed: {storage_commitment}") From e42f03d68336607b3943de42b614d7c57ec5e54d Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 25 Nov 2025 16:33:44 -0800 Subject: [PATCH 05/67] updated pallet --- .../core/extrinsics/pallets/mev_shield.py | 66 ------------------- 1 file changed, 66 deletions(-) diff --git a/bittensor/core/extrinsics/pallets/mev_shield.py b/bittensor/core/extrinsics/pallets/mev_shield.py index 3996914d87..cef4995bc8 100644 --- a/bittensor/core/extrinsics/pallets/mev_shield.py +++ b/bittensor/core/extrinsics/pallets/mev_shield.py @@ -1,7 +1,5 @@ from dataclasses import dataclass -from scalecodec import GenericCall - from .base import CallBuilder as _BasePallet, Call @@ -30,70 +28,6 @@ class MevShield(_BasePallet): response = await async_subtensor.sign_and_send_extrinsic(call=call, ...) """ - def announce_next_key( - self, - public_key: bytes, - epoch: int, - ) -> Call: - """Returns GenericCall instance for MevShield function announce_next_key. - - This function allows Aura validators to announce the next ML-KEM-768 public key that will be used for encryption - in the next block. The key will be rotated from NextKey to CurrentKey at the beginning of the next block. - - Parameters: - public_key: The ML-KEM-768 public key as bytes. Must be exactly 1184 bytes for ML-KEM-768. - epoch: The epoch number associated with this key. - - Returns: - GenericCall instance ready for extrinsic submission. - - Note: - Only current Aura validators can call this function. The public_key length must be exactly 1184 bytes - (MAX_KYBER768_PK_LENGTH). This is enforced by the pallet. - """ - return self.create_composed_call( - public_key=public_key, - epoch=epoch, - ) - - def execute_revealed( - self, - id: str, - signer: str, - nonce: int, - call: "GenericCall", - signature: bytes, - ) -> Call: - """Returns GenericCall instance for MevShield function execute_revealed. - - This function is executed by the block author (validator) to execute a decrypted extrinsic. It verifies the - commitment, signature, and nonce before dispatching the inner call. - - Parameters: - id: The submission ID (hash of signer + commitment + ciphertext). Must be a hex string with "0x" prefix. - signer: The SS58 address of the account that originally submitted the encrypted extrinsic. - nonce: The nonce that was used when creating the payload_core. - call: The GenericCall object to execute (the decrypted call). - signature: The signature bytes (MultiSignature format) that was included in the encrypted payload. - - Returns: - GenericCall instance ready for extrinsic submission. - - Note: - This is an unsigned extrinsic that can only be called by the block author. It verifies: - 1. Commitment matches the stored submission - 2. Signature is valid over "mev-shield:v1" + genesis_hash + payload_core - 3. Nonce matches the current account nonce - 4. Then dispatches the inner call from the signer - """ - return self.create_composed_call( - id=id, - signer=signer, - nonce=nonce, - call=call, - signature=signature, - ) - def submit_encrypted( self, commitment: str, From bb5ba56a921a17f14d15550f8fe1cdcb84e88425 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 25 Nov 2025 16:34:15 -0800 Subject: [PATCH 06/67] workable draft --- bittensor/core/extrinsics/mev_shield.py | 164 +++++++++++++++++ bittensor/core/subtensor.py | 222 ++++++++++++++++++++++++ 2 files changed, 386 insertions(+) create mode 100644 bittensor/core/extrinsics/mev_shield.py diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py new file mode 100644 index 0000000000..a5cd534ba1 --- /dev/null +++ b/bittensor/core/extrinsics/mev_shield.py @@ -0,0 +1,164 @@ +"""Module provides sync MEV Shield extrinsics.""" + +from scalecodec.utils.ss58 import ss58_decode +import hashlib +import struct +from typing import TYPE_CHECKING, Optional + +import bittensor_drand +from bittensor_drand import encrypt_mlkem768 + +from bittensor.core.extrinsics.pallets import MevShield +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils.btlogging import logging + +if TYPE_CHECKING: + from bittensor.core.subtensor import Subtensor + from bittensor_wallet import Wallet, Keypair + from scalecodec.types import GenericCall + + +def get_mev_commitment_and_ciphertext( + call: "GenericCall", + signer_keypair: "Keypair", + genesis_hash: str, + nonce: int, + ml_kem_768_public_key: bytes, +) -> tuple[str, bytes, bytes, bytes]: + # Create payload_core: signer (32B) + nonce (u32 LE) + SCALE(call) + decoded_ss58 = ss58_decode(signer_keypair.ss58_address) + decoded_ss58_cut = ( + decoded_ss58[2:] if decoded_ss58.startswith("0x") else decoded_ss58 + ) + signer_bytes = bytes.fromhex(decoded_ss58_cut) # 32 bytes + + # Ensure nonce is u32 (as in Rust) + nonce_u32 = nonce & 0xFFFFFFFF + nonce_bytes = struct.pack(" ExtrinsicResponse: + """ + Submits an encrypted extrinsic to the MEV Shield pallet. + + This function encrypts a call using ML-KEM-768 + XChaCha20Poly1305 and submits it to the MevShield pallet. The + extrinsic remains encrypted in the transaction pool until it is included in a block and decrypted by validators. + + Parameters: + subtensor: The Subtensor client instance used for blockchain interaction. + wallet: The wallet used to sign the extrinsic (must be unlocked, coldkey will be used for signing). + call: The GenericCall object to encrypt and submit. + signer_keypair: The Keypair object used for signing the inner call. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Raises: + ValueError: If NextKey is not available in storage or encryption fails. + SubstrateRequestException: If the extrinsic fails to be submitted or included. + + Note: + The encryption uses the public key from NextKey storage, which rotates every block. The payload structure is: + payload_core = signer_bytes (32B) + nonce (u32 LE, 4B) + SCALE(call) + plaintext = payload_core + b"\\x01" + signature (64B for sr25519) + commitment = blake2_256(payload_core) + """ + try: + if not ( + unlocked := ExtrinsicResponse.unlock_wallet(wallet, raise_error) + ).success: + return unlocked + + ml_kem_768_public_key = subtensor.get_mev_shield_next_key() + if ml_kem_768_public_key is None: + return ExtrinsicResponse(False, "NextKey is not available in storage.") + + genesis_hash = subtensor.get_block_hash(block=0) + nonce = subtensor.substrate.get_account_nonce(signer_keypair.ss58_address) + + mev_commitment, mev_ciphertext, payload_core, signature = ( + get_mev_commitment_and_ciphertext( + call=call, + signer_keypair=signer_keypair, + genesis_hash=genesis_hash, + nonce=nonce, + ml_kem_768_public_key=ml_kem_768_public_key, + ) + ) + + extrinsic_call = MevShield(subtensor).submit_encrypted( + commitment=mev_commitment, + ciphertext=mev_ciphertext, + ) + + response = subtensor.sign_and_send_extrinsic( + wallet=wallet, + call=extrinsic_call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if response.success: + logging.debug("[green]Encrypted extrinsic submitted successfully.[/green]") + response.data = { + "commitment": mev_commitment, + "ciphertext": mev_ciphertext, + "nonce": nonce, + "payload_core": payload_core, + "signature": signature, + "submitting_id": extrinsic_call.call_hash, + } + else: + logging.error(f"[red]{response.message}[/red]") + + return response + + except Exception as error: + return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 534c1aa3f8..63a88b98da 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -113,6 +113,7 @@ unstake_multiple_extrinsic, ) from bittensor.core.extrinsics.utils import get_transfer_fn_params +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.weights import ( commit_timelocked_weights_extrinsic, commit_weights_extrinsic, @@ -2232,6 +2233,176 @@ def get_metagraph_info( return MetagraphInfo.from_dict(query.value) + def get_mev_shield_current_key( + self, block: Optional[int] = None + ) -> Optional[bytes]: + """ + Retrieves the CurrentKey from the MevShield pallet storage. + + The CurrentKey contains the ML-KEM-768 public key that is currently being used for encryption in this block. + This key is rotated from NextKey at the beginning of each block. + + Parameters: + block: The blockchain block number at which to perform the query. If None, uses the current block. + + Returns: + The ML-KEM-768 public key as bytes (1184 bytes for ML-KEM-768) + + Note: + If CurrentKey is not set (None in storage), this function returns None. This can happen if no validator has + announced a key yet. + """ + block_hash = self.determine_block_hash(block=block) + query = self.substrate.query( + module="MevShield", + storage_function="CurrentKey", + block_hash=block_hash, + ) + + if query is None: + return None + + public_key_bytes = bytes(next(iter(query))) + + # Validate public_key size for ML-KEM-768 (must be exactly 1184 bytes) + MLKEM768_PUBLIC_KEY_SIZE = 1184 + if len(public_key_bytes) != MLKEM768_PUBLIC_KEY_SIZE: + raise ValueError( + f"Invalid ML-KEM-768 public key size: {len(public_key_bytes)} bytes. " + f"Expected exactly {MLKEM768_PUBLIC_KEY_SIZE} bytes." + ) + + return public_key_bytes + + def get_mev_shield_next_key(self, block: Optional[int] = None) -> Optional[bytes]: + """ + Retrieves the NextKey from the MevShield pallet storage. + + The NextKey contains the ML-KEM-768 public key that will be used for encryption in the next block. This key is + rotated from NextKey to CurrentKey at the beginning of each block. + + Parameters: + block: The blockchain block number at which to perform the query. If None, uses the current block. + + Returns: + The ML-KEM-768 public key as bytes (1184 bytes for ML-KEM-768) + + Note: + If NextKey is not set (None in storage), this function returns None. This can happen if no validator has + announced the next key yet. + """ + block_hash = self.determine_block_hash(block=block) + query = self.substrate.query( + module="MevShield", + storage_function="NextKey", + block_hash=block_hash, + ) + + if query is None: + return None + + public_key_bytes = bytes(next(iter(query))) + + # Validate public_key size for ML-KEM-768 (must be exactly 1184 bytes) + MLKEM768_PUBLIC_KEY_SIZE = 1184 + if len(public_key_bytes) != MLKEM768_PUBLIC_KEY_SIZE: + raise ValueError( + f"Invalid ML-KEM-768 public key size: {len(public_key_bytes)} bytes. " + f"Expected exactly {MLKEM768_PUBLIC_KEY_SIZE} bytes." + ) + + return public_key_bytes + + def get_mev_shield_submission( + self, + submission_id: str, + block: Optional[int] = None, + ) -> Optional[dict[str, str | int | bytes]]: + """ + Retrieves Submission from the MevShield pallet storage. + + If submission_id is provided, returns a single submission. If submission_id is None, returns all submissions from + the storage map. + + Parameters: + submission_id: The hash ID of the submission. Can be a hex string with "0x" prefix or bytes. If None, + returns all submissions. + block: The blockchain block number at which to perform the query. If None, uses the current block. + + Returns: + If submission_id is provided: A dictionary containing the submission data if found, None otherwise. The + dictionary contains: + - author: The SS58 address of the account that submitted the encrypted extrinsic + - commitment: The blake2_256 hash of the payload_core (as hex string with "0x" prefix) + - ciphertext: The encrypted blob as bytes (format: [u16 kem_len][kem_ct][nonce24][aead_ct]) + - submitted_in: The block number when the submission was created + + If submission_id is None: A dictionary mapping submission IDs (as hex strings) to submission dictionaries. + + Note: + If a specific submission does not exist in storage, this function returns None. If querying all submissions + and none exist, returns an empty dictionary. + """ + block_hash = self.determine_block_hash(block=block) + submission_id = ( + submission_id[2:] if submission_id.startswith("0x") else submission_id + ) + submission_id_bytes = bytes.fromhex(submission_id) + + query = self.substrate.query( + module="MevShield", + storage_function="Submissions", + params=[submission_id_bytes], + block_hash=block_hash, + ) + + if query is None or not isinstance(query, dict): + return None + + autor = decode_account_id(query.get("author")) + commitment = bytes(query.get("commitment")[0]) + ciphertext = bytes(query.get("ciphertext")[0]) + submitted_in = query.get("submitted_in") + + return { + "author": autor, + "commitment": commitment, + "ciphertext": ciphertext, + "submitted_in": submitted_in, + } + + def get_mev_shield_submissions( + self, + block: Optional[int] = None, + ) -> Optional[dict[str, dict[str, str | int]]]: + """ + Retrieves Submission(s) from the MevShield pallet storage. + + Parameters: + + Returns: + + """ + block_hash = self.determine_block_hash(block=block) + query = self.substrate.query_map( + module="MevShield", + storage_function="Submissions", + block_hash=block_hash, + ) + + result = {} + for q in query: + key, value = q + value = value.value + result["0x" + bytes(key[0]).hex()] = { + "author": decode_account_id(value.get("author")), + "commitment": bytes(value.get("commitment")[0]), + "ciphertext": bytes(value.get("ciphertext")[0]), + "submitted_in": value.get("submitted_in"), + } + + return result if result else None + def get_minimum_required_stake(self) -> Balance: """ Returns the minimum required stake for nominators in the Subtensor network. @@ -5000,6 +5171,57 @@ def kill_pure_proxy( wait_for_finalization=wait_for_finalization, ) + def mev_submit_encrypted( + self, + wallet: "Wallet", + call: "GenericCall", + signer_keypair: "Keypair", + period: Optional[int] = DEFAULT_PERIOD, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + ) -> ExtrinsicResponse: + """ + Submits an encrypted extrinsic to the MEV Shield pallet. + + This function encrypts a call using ML-KEM-768 + XChaCha20Poly1305 and submits it to the MevShield pallet. The + extrinsic remains encrypted in the transaction pool until it is included in a block and decrypted by validators. + + Parameters: + wallet: The wallet used to sign the extrinsic (must be unlocked, coldkey will be used for signing). + call: The GenericCall object to encrypt and submit. + signer_keypair: The keypair used to sign the inner call. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Raises: + ValueError: If NextKey is not available in storage or encryption fails. + SubstrateRequestException: If the extrinsic fails to be submitted or included. + + Note: + The encryption uses the public key from NextKey storage, which rotates every block. The payload structure is: + payload_core = signer_bytes (32B) + nonce (u32 LE, 4B) + SCALE(call) + plaintext = payload_core + b"\\x01" + signature (64B for sr25519) + commitment = blake2_256(payload_core) + """ + return submit_encrypted_extrinsic( + subtensor=self, + wallet=wallet, + call=call, + signer_keypair=signer_keypair, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + def modify_liquidity( self, wallet: "Wallet", From 770a99c14add3eeeb67ec3f4e00958b0d778993f Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 25 Nov 2025 18:19:21 -0800 Subject: [PATCH 07/67] apply signer_keypair logic as third keypair --- bittensor/core/extrinsics/mev_shield.py | 10 ++++++++-- bittensor/core/subtensor.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index a5cd534ba1..f9dd65d32e 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -71,7 +71,7 @@ def submit_encrypted_extrinsic( subtensor: "Subtensor", wallet: "Wallet", call: "GenericCall", - signer_keypair: "Keypair", + signer_keypair: Optional["Keypair"] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -114,9 +114,13 @@ def submit_encrypted_extrinsic( ).success: return unlocked + # Use wallet.coldkey as default signer if signer_keypair is not provided + if signer_keypair is None: + signer_keypair = wallet.coldkey + ml_kem_768_public_key = subtensor.get_mev_shield_next_key() if ml_kem_768_public_key is None: - return ExtrinsicResponse(False, "NextKey is not available in storage.") + return ExtrinsicResponse.from_exception(raise_error=raise_error, error=ValueError("MEV Shield NextKey not available in storage.")) genesis_hash = subtensor.get_block_hash(block=0) nonce = subtensor.substrate.get_account_nonce(signer_keypair.ss58_address) @@ -139,6 +143,8 @@ def submit_encrypted_extrinsic( response = subtensor.sign_and_send_extrinsic( wallet=wallet, call=extrinsic_call, + sign_with="hotkey", + use_nonce=False, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 63a88b98da..02abda61a9 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -5175,7 +5175,7 @@ def mev_submit_encrypted( self, wallet: "Wallet", call: "GenericCall", - signer_keypair: "Keypair", + signer_keypair: Optional["Keypair"] = None, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, From aa0d5a3e2f62a13dd3e9d2782f448a138b01b748 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 25 Nov 2025 18:20:26 -0800 Subject: [PATCH 08/67] probably tests are broken --- tests/e2e_tests/test_mev_shield.py | 52 ++++++++++++------------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/tests/e2e_tests/test_mev_shield.py b/tests/e2e_tests/test_mev_shield.py index 4ede73df8d..8fb51f0681 100644 --- a/tests/e2e_tests/test_mev_shield.py +++ b/tests/e2e_tests/test_mev_shield.py @@ -1,14 +1,12 @@ """E2E tests for MEV Shield functionality.""" +import pytest import hashlib from bittensor_drand import generate_mlkem768_keypair from bittensor_wallet import Wallet -from bittensor.core.extrinsics.mev_shield import ( - announce_next_key_extrinsic, - submit_encrypted_extrinsic, -) +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -21,12 +19,14 @@ ) -def add_balance_to_wallet_hk(subtensor, wallet: "Wallet", tao_amount: int) -> ExtrinsicResponse: +def add_balance_to_wallet_hk( + subtensor, wallet: "Wallet", tao_amount: int +) -> ExtrinsicResponse: """Adds 100 TAO to Alice's HK balance.""" return subtensor.extrinsics.transfer( wallet=wallet, destination_ss58=wallet.hotkeypub.ss58_address, - amount=Balance.from_tao(tao_amount) + amount=Balance.from_tao(tao_amount), ) @@ -104,11 +104,10 @@ def test_mev_shield_announce_next_key(subtensor, alice_wallet): ) assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" - # Wait a few blocks after setting tempo to ensure state is synchronized - # This prevents "Transaction is outdated" errors by allowing the chain to process - # the tempo change and stabilize before we submit the announce transaction. + # Wait for tempo transaction to be included in a block + # This ensures the tempo change is processed before we submit the announce transaction current_block = subtensor.block - subtensor.wait_for_block(current_block + 5) + subtensor.wait_for_block(current_block + 1) # Generate ML-KEM-768 keypair using bittensor_drand public_key_bytes = generate_mlkem768_keypair() @@ -117,6 +116,7 @@ def test_mev_shield_announce_next_key(subtensor, alice_wallet): ) # Get current epoch and prepare next epoch + # We use current_epoch + 1 because we're announcing the key for the NEXT epoch current_epoch = subtensor.mev_shield.get_mev_shield_epoch() next_epoch = current_epoch + 1 @@ -199,17 +199,11 @@ def test_mev_shield_submit_encrypted_full_flow(subtensor, bob_wallet, alice_wall ) assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" - # Wait for first block of next epoch to avoid "Transaction is outdated" error - # MEV Shield epoch validation requires that the announced epoch matches the current epoch - # when the transaction is processed. By waiting for the start of the next epoch, we ensure - # that our announced epoch (current_epoch + 1) will be valid when the transaction executes. - next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(netuid=0) - if next_epoch_start_block: - logging.info(f"Waiting for next epoch start block: {next_epoch_start_block}") - subtensor.wait_for_block(next_epoch_start_block) - # Wait one more block to ensure all state is synchronized - current_block = subtensor.block - subtensor.wait_for_block(current_block + 1) + # Wait a few blocks after setting tempo to ensure state is synchronized + # This prevents "Transaction is outdated" errors by allowing the chain to process + # the tempo change and stabilize before we submit the announce transaction. + current_block = subtensor.block + subtensor.wait_for_block(current_block + 5) # Generate and announce a key first using alice_wallet (which is a validator in localnet) public_key_bytes = generate_mlkem768_keypair() @@ -451,17 +445,11 @@ def test_mev_shield_commitment_verification(subtensor, bob_wallet, alice_wallet) ) assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" - # Wait for first block of next epoch to avoid "Transaction is outdated" error - # MEV Shield epoch validation requires that the announced epoch matches the current epoch - # when the transaction is processed. By waiting for the start of the next epoch, we ensure - # that our announced epoch (current_epoch + 1) will be valid when the transaction executes. - next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(netuid=0) - if next_epoch_start_block: - logging.info(f"Waiting for next epoch start block: {next_epoch_start_block}") - subtensor.wait_for_block(next_epoch_start_block) - # Wait one more block to ensure all state is synchronized - current_block = subtensor.block - subtensor.wait_for_block(current_block + 1) + # Wait a few blocks after setting tempo to ensure state is synchronized + # This prevents "Transaction is outdated" errors by allowing the chain to process + # the tempo change and stabilize before we submit the announce transaction. + current_block = subtensor.block + subtensor.wait_for_block(current_block + 5) # Generate and announce a key first using alice_wallet (which is a validator in localnet) public_key_bytes = generate_mlkem768_keypair() From 51ca4e1ca9cc143bbebe65ebd7d01f452acfa7a7 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 25 Nov 2025 18:20:36 -0800 Subject: [PATCH 09/67] ruff --- bittensor/core/extrinsics/mev_shield.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index f9dd65d32e..16e4190583 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -120,7 +120,10 @@ def submit_encrypted_extrinsic( ml_kem_768_public_key = subtensor.get_mev_shield_next_key() if ml_kem_768_public_key is None: - return ExtrinsicResponse.from_exception(raise_error=raise_error, error=ValueError("MEV Shield NextKey not available in storage.")) + return ExtrinsicResponse.from_exception( + raise_error=raise_error, + error=ValueError("MEV Shield NextKey not available in storage."), + ) genesis_hash = subtensor.get_block_hash(block=0) nonce = subtensor.substrate.get_account_nonce(signer_keypair.ss58_address) From 2a8709fc0d16c1c06185a0e188ce9b0897f14a71 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:22:54 -0800 Subject: [PATCH 10/67] bump ASI version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b6e6a387fe..430cd916d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ dependencies = [ "uvicorn", "bittensor-drand>=1.0.0,<2.0.0", "bittensor-wallet>=4.0.0,<5.0", - "async-substrate-interface>=1.5.12" + "async-substrate-interface>=1.5.13" ] [project.optional-dependencies] From f700b4a0740f412bd125c42953d6999ed88580c5 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:31:27 -0800 Subject: [PATCH 11/67] add pallets to easy imports --- bittensor/utils/easy_imports.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index f4dd89902d..fa26b0b745 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -100,6 +100,7 @@ UnknownSynapseError, UnstakeError, ) +from bittensor.core.extrinsics import pallets from bittensor.core.metagraph import Metagraph from bittensor.core.settings import BLOCKTIME from bittensor.core.stream import StreamingSynapse @@ -229,6 +230,7 @@ "tao", "rao", "logging", + "pallets", "MockSubtensor", "SubnetsAPI", "trace", From 7d221ea1f7e9f392f4ca2fdf66bcb0013087a95a Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:31:50 -0800 Subject: [PATCH 12/67] add get_event_data, get_mev_commitment_and_ciphertext to utils --- bittensor/core/extrinsics/utils.py | 99 ++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 983c8cbc12..5ddd96ac72 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -1,7 +1,11 @@ """Module with helper functions for extrinsics.""" +import hashlib from typing import TYPE_CHECKING, Optional, Union +from bittensor_drand import encrypt_mlkem768, mlkem_kdf_id +from scalecodec import ss58_decode + from bittensor.core.extrinsics.pallets import Sudo from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -10,6 +14,8 @@ from bittensor_wallet import Wallet from bittensor.core.chain_data import StakeInfo from bittensor.core.subtensor import Subtensor + from scalecodec.types import GenericCall + from bittensor_wallet.keypair import Keypair def get_old_stakes( @@ -207,3 +213,96 @@ def apply_pure_proxy_data( raise RuntimeError(message) return response.with_log("warning") + + +def get_mev_commitment_and_ciphertext( + call: "GenericCall", + signer_keypair: "Keypair", + genesis_hash: str, + ml_kem_768_public_key: bytes, +) -> tuple[str, bytes, bytes, bytes]: + """ + Builds MEV Shield payload and encrypts it using ML-KEM-768 + XChaCha20Poly1305. + + This function constructs the payload structure required for MEV Shield encryption and performs the encryption + process. The payload binds the transaction to a specific key epoch using the key_hash, which replaces nonce-based + replay protection. + + Parameters: + call: The GenericCall object representing the inner call to be encrypted and executed. + signer_keypair: The Keypair used for signing the inner call payload. The signer's AccountId32 (32 bytes) is + embedded in the payload_core. + genesis_hash: The genesis block hash as a hex string (with or without "0x" prefix). Used for chain-bound + signature domain separation. + ml_kem_768_public_key: The ML-KEM-768 public key bytes (1184 bytes) from NextKey storage. This key is used for + encryption and its hash binds the transaction to the key epoch. + + Returns: + A tuple containing: + - commitment_hex (str): Hex string of the Blake2-256 hash of payload_core (32 bytes). + - ciphertext (bytes): Encrypted blob containing plaintext. + - payload_core (bytes): Raw payload bytes before encryption. + - signature (bytes): MultiSignature (64 bytes for sr25519). + """ + # Create payload_core: signer (32B) + key_hash (32B Blake2-256 hash) + SCALE(call) + decoded_ss58 = ss58_decode(signer_keypair.ss58_address) + decoded_ss58_cut = ( + decoded_ss58[2:] if decoded_ss58.startswith("0x") else decoded_ss58 + ) + signer_bytes = bytes.fromhex(decoded_ss58_cut) # 32 bytes + + # Compute key_hash = Blake2-256(NextKey_bytes) + # This binds the transaction to the key epoch at submission time + key_hash_bytes = hashlib.blake2b( + ml_kem_768_public_key, digest_size=32 + ).digest() # 32 bytes + + scale_call_bytes = bytes(call.data.data) # SCALE encoded call + mev_shield_version = mlkem_kdf_id() + + # Fix genesis_hash processing + genesis_hash_clean = ( + genesis_hash[2:] if genesis_hash.startswith("0x") else genesis_hash + ) + genesis_hash_bytes = bytes.fromhex(genesis_hash_clean) + + payload_core = signer_bytes + key_hash_bytes + scale_call_bytes + + # Sign payload: coldkey.sign(b"mev-shield:v1" + genesis_hash + payload_core) + message_to_sign = ( + b"mev-shield:" + mev_shield_version + genesis_hash_bytes + payload_core + ) + + signature = signer_keypair.sign(message_to_sign) + + # Create plaintext: payload_core + b"\x01" + signature + plaintext = payload_core + b"\x01" + signature + + # Getting ciphertext (encrypting plaintext using ML-KEM-768) + ciphertext = encrypt_mlkem768(ml_kem_768_public_key, plaintext) + + # Compute commitment: blake2_256(payload_core) + commitment_hash = hashlib.blake2b(payload_core, digest_size=32).digest() + commitment_hex = "0x" + commitment_hash.hex() + + return commitment_hex, ciphertext, payload_core, signature + + +def get_event_data(triggered_events: list, event_id: str) -> Optional[dict]: + """ + Extracts event data from triggered events by event ID. + + Searches through a list of triggered events and returns the attributes dictionary for the first event matching the + specified event_id. + + Parameters: + triggered_events: List of event dictionaries, typically from ExtrinsicReceipt.triggered_events. Each event + should have an "event_id" key and an "attributes" key. + event_id: The event identifier to search for (e.g., "EncryptedSubmitted", "DecryptedExecuted"). + + Returns: + The attributes dictionary of the matching event, or None if no matching event is found.""" + for event in triggered_events: + if event["event_id"] == event_id: + return event["attributes"] + return None From f0ac97774f11991fac12c3b081d328429c8e5fcd Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:32:36 -0800 Subject: [PATCH 13/67] update SubtensorApi --- bittensor/extras/subtensor_api/mev_shield.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bittensor/extras/subtensor_api/mev_shield.py b/bittensor/extras/subtensor_api/mev_shield.py index eb778ce0c8..75e97ddf50 100644 --- a/bittensor/extras/subtensor_api/mev_shield.py +++ b/bittensor/extras/subtensor_api/mev_shield.py @@ -10,11 +10,8 @@ class MevShield: def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): # Storage queries self.get_mev_shield_current_key = subtensor.get_mev_shield_current_key - self.get_mev_shield_epoch = subtensor.get_mev_shield_epoch self.get_mev_shield_next_key = subtensor.get_mev_shield_next_key self.get_mev_shield_submission = subtensor.get_mev_shield_submission # Extrinsics - self.mev_announce_next_key = subtensor.mev_announce_next_key - self.mev_execute_revealed = subtensor.mev_execute_revealed self.mev_submit_encrypted = subtensor.mev_submit_encrypted From 0bda5c1a795d76524ab2a280b67928ac2eccb507 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:33:00 -0800 Subject: [PATCH 14/67] add sync `submit_encrypted_extrinsic` --- bittensor/core/extrinsics/mev_shield.py | 169 ++++++++++++++++-------- 1 file changed, 113 insertions(+), 56 deletions(-) diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index 16e4190583..50888ddd90 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -1,14 +1,11 @@ """Module provides sync MEV Shield extrinsics.""" -from scalecodec.utils.ss58 import ss58_decode -import hashlib -import struct from typing import TYPE_CHECKING, Optional -import bittensor_drand -from bittensor_drand import encrypt_mlkem768 +from async_substrate_interface import ExtrinsicReceipt from bittensor.core.extrinsics.pallets import MevShield +from bittensor.core.extrinsics.utils import get_event_data, get_mev_commitment_and_ciphertext from bittensor.core.types import ExtrinsicResponse from bittensor.utils.btlogging import logging @@ -18,53 +15,75 @@ from scalecodec.types import GenericCall -def get_mev_commitment_and_ciphertext( - call: "GenericCall", - signer_keypair: "Keypair", - genesis_hash: str, - nonce: int, - ml_kem_768_public_key: bytes, -) -> tuple[str, bytes, bytes, bytes]: - # Create payload_core: signer (32B) + nonce (u32 LE) + SCALE(call) - decoded_ss58 = ss58_decode(signer_keypair.ss58_address) - decoded_ss58_cut = ( - decoded_ss58[2:] if decoded_ss58.startswith("0x") else decoded_ss58 - ) - signer_bytes = bytes.fromhex(decoded_ss58_cut) # 32 bytes - - # Ensure nonce is u32 (as in Rust) - nonce_u32 = nonce & 0xFFFFFFFF - nonce_bytes = struct.pack(" Optional["ExtrinsicReceipt"]: + """ + Searches for an extrinsic containing a specific MEV Shield event in subsequent blocks. - # Fix genesis_hash processing - genesis_hash_clean = ( - genesis_hash[2:] if genesis_hash.startswith("0x") else genesis_hash - ) - genesis_hash_bytes = bytes.fromhex(genesis_hash_clean) + This function iterates through blocks starting from the specified block hash and searches for extrinsics that + contain a MEV Shield event (DecryptedExecuted or DecryptedRejected) matching the provided wrapper_id and signer. It + checks each extrinsic's triggered events to find the matching event. - payload_core = signer_bytes + nonce_bytes + scale_call_bytes + Parameters: + subtensor: The Subtensor instance used for blockchain queries. + signer_ss58: The SS58 address of the signer account. Used to verify that the event belongs to the correct + transaction (matches the "signer" attribute in the event). + event_id: The event identifier to search for. Typically "DecryptedExecuted" or "DecryptedRejected" for MEV + Shield transactions. + event_hash_id: The wrapper_id (hash of (author, commitment, ciphertext)) to match. This uniquely identifies a + specific MEV Shield submission. + start_block_hash: The hash of the block where the search should begin. Usually the block where submit_encrypted + was included. + blocks_ahead: Maximum number of blocks to search ahead from the start block. Defaults to 5 blocks. The function + will check blocks from start_block + 1 to start_block + blocks_ahead (the start block itself is not checked, + as execute_revealed will be in subsequent blocks). - # Sign payload: coldkey.sign(b"mev-shield:v1" + genesis_hash + payload_core) - message_to_sign = ( - b"mev-shield:" + mev_shield_version + genesis_hash_bytes + payload_core - ) + Returns: + The ExtrinsicReceipt object for the extrinsic containing the matching event, or None if the event is not found + within the specified block range. + """ + start_block_number = subtensor.substrate.get_block_number(start_block_hash) - signature = signer_keypair.sign(message_to_sign) + for offset in range(1, blocks_ahead + 1): + current_block_number = start_block_number + offset - # Create plaintext: payload_core + b"\x01" + signature - plaintext = payload_core + b"\x01" + signature + try: + current_block_hash = subtensor.substrate.get_block_hash( + current_block_number + ) + extrinsics = subtensor.substrate.get_extrinsics(current_block_hash) + except Exception as e: + logging.debug( + f"Error getting extrinsics for block `{current_block_number}`: {e}" + ) + continue + + for idx, e in enumerate(extrinsics): + extrinsic_ = ExtrinsicReceipt( + substrate=subtensor.substrate, + extrinsic_hash=e.extrinsic_hash, + block_hash=current_block_hash, + extrinsic_idx=idx, + ) - # Getting ciphertext (encrypting plaintext using ML-KEM-768) - ciphertext = encrypt_mlkem768(ml_kem_768_public_key, plaintext) + if triggered_events := extrinsic_.triggered_events: + event_data = get_event_data(triggered_events, event_id) + if ( + event_data + and event_hash_id == event_data["id"] + and signer_ss58 == event_data["signer"] + ): + return extrinsic_ - # Compute commitment: blake2_256(payload_core) - commitment_hash = hashlib.blake2b(payload_core, digest_size=32).digest() - commitment_hex = "0x" + commitment_hash.hex() + subtensor.wait_for_block() - return commitment_hex, ciphertext, payload_core, signature + return None def submit_encrypted_extrinsic( @@ -76,6 +95,7 @@ def submit_encrypted_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + wait_for_revealed_execution: Optional[int] = 5, ) -> ExtrinsicResponse: """ Submits an encrypted extrinsic to the MEV Shield pallet. @@ -94,6 +114,11 @@ def submit_encrypted_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Maximum number of blocks to wait for the DecryptedExecuted event, indicating that + node validators have successfully decrypted and executed the inner call via execute_revealed. If None, the + function will not wait for revealed execution. If an integer (default: 5), the function will poll up to that + many blocks after inclusion, checking for the DecryptedExecuted event matching this submission's commitment. + The function returns immediately if the event is found before the block limit is reached. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -104,13 +129,18 @@ def submit_encrypted_extrinsic( Note: The encryption uses the public key from NextKey storage, which rotates every block. The payload structure is: - payload_core = signer_bytes (32B) + nonce (u32 LE, 4B) + SCALE(call) + payload_core = signer_bytes (32B) + key_hash (32B Blake2-256 hash of NextKey) + SCALE(call) plaintext = payload_core + b"\\x01" + signature (64B for sr25519) commitment = blake2_256(payload_core) + + The key_hash binds the transaction to the key epoch at submission time and replaces nonce-based replay + protection. """ try: if not ( - unlocked := ExtrinsicResponse.unlock_wallet(wallet, raise_error) + unlocked := ExtrinsicResponse.unlock_wallet( + wallet, raise_error, unlock_type="both" + ) ).success: return unlocked @@ -120,20 +150,22 @@ def submit_encrypted_extrinsic( ml_kem_768_public_key = subtensor.get_mev_shield_next_key() if ml_kem_768_public_key is None: - return ExtrinsicResponse.from_exception( - raise_error=raise_error, - error=ValueError("MEV Shield NextKey not available in storage."), - ) + # Fallback to CurrentKey if NextKey is not available + current_key_result = subtensor.get_mev_shield_current_key() + if current_key_result is None: + return ExtrinsicResponse.from_exception( + raise_error=raise_error, + error=ValueError("MEV Shield NextKey not available in storage."), + ) + ml_kem_768_public_key = current_key_result genesis_hash = subtensor.get_block_hash(block=0) - nonce = subtensor.substrate.get_account_nonce(signer_keypair.ss58_address) mev_commitment, mev_ciphertext, payload_core, signature = ( get_mev_commitment_and_ciphertext( call=call, signer_keypair=signer_keypair, genesis_hash=genesis_hash, - nonce=nonce, ml_kem_768_public_key=ml_kem_768_public_key, ) ) @@ -146,8 +178,6 @@ def submit_encrypted_extrinsic( response = subtensor.sign_and_send_extrinsic( wallet=wallet, call=extrinsic_call, - sign_with="hotkey", - use_nonce=False, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -155,15 +185,42 @@ def submit_encrypted_extrinsic( ) if response.success: - logging.debug("[green]Encrypted extrinsic submitted successfully.[/green]") response.data = { "commitment": mev_commitment, "ciphertext": mev_ciphertext, - "nonce": nonce, + "ml_kem_768_public_key": ml_kem_768_public_key, "payload_core": payload_core, "signature": signature, "submitting_id": extrinsic_call.call_hash, } + if wait_for_revealed_execution is not None and isinstance( + wait_for_revealed_execution, int + ): + triggered_events = response.extrinsic_receipt.triggered_events + event_hash_id = get_event_data(triggered_events, "EncryptedSubmitted")[ + "id" + ] + + revealed_extrinsic_receipt = find_revealed_extrinsic( + subtensor=subtensor, + signer_ss58=signer_keypair.ss58_address, + event_id="DecryptedExecuted", + event_hash_id=event_hash_id, + start_block_hash=response.extrinsic_receipt.block_hash, + blocks_ahead=wait_for_revealed_execution, + ) + if revealed_extrinsic_receipt: + response.data.update( + {"revealed_extrinsic_receipt": revealed_extrinsic_receipt} + ) + else: + response.success = False + response.error = RuntimeError(f"DecryptedExecuted event not found.") + return response.from_exception( + raise_error=raise_error, error=response.error + ) + + logging.debug("[green]Encrypted extrinsic submitted successfully.[/green]") else: logging.error(f"[red]{response.message}[/red]") From a128f2999b17c4afd8aae3737c31f5c19f33e8e5 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:33:10 -0800 Subject: [PATCH 15/67] add sync e2e test --- tests/e2e_tests/test_mev_shield.py | 591 +++-------------------------- 1 file changed, 63 insertions(+), 528 deletions(-) diff --git a/tests/e2e_tests/test_mev_shield.py b/tests/e2e_tests/test_mev_shield.py index 8fb51f0681..0ec530fba4 100644 --- a/tests/e2e_tests/test_mev_shield.py +++ b/tests/e2e_tests/test_mev_shield.py @@ -1,561 +1,96 @@ """E2E tests for MEV Shield functionality.""" -import pytest -import hashlib - -from bittensor_drand import generate_mlkem768_keypair from bittensor_wallet import Wallet -from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic -from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.extrinsics import pallets from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging from tests.e2e_tests.utils import ( - AdminUtils, - NETUID, - SUDO_SET_TEMPO, TestSubnet, + ACTIVATE_SUBNET, + REGISTER_NEURON, + REGISTER_SUBNET, ) -def add_balance_to_wallet_hk( - subtensor, wallet: "Wallet", tao_amount: int -) -> ExtrinsicResponse: - """Adds 100 TAO to Alice's HK balance.""" - return subtensor.extrinsics.transfer( - wallet=wallet, - destination_ss58=wallet.hotkeypub.ss58_address, - amount=Balance.from_tao(tao_amount), - ) - - -def test_mev_shield_storage_queries(subtensor): - """Tests querying MevShield storage items. - - Steps: - 1. Query CurrentKey (may be None) - 2. Query NextKey (may be None) - 3. Query Epoch (always >= 0) - 4. Query Submissions (all, returns dict) - """ - - # Query Epoch - should always be available - epoch = subtensor.mev_shield.get_mev_shield_epoch() - assert isinstance(epoch, int), f"Epoch should be int, got {type(epoch)}" - assert epoch >= 0, f"Epoch should be non-negative, got {epoch}" - - # Query CurrentKey - may be None if no key announced yet - current_key = subtensor.mev_shield.get_mev_shield_current_key() - if current_key is not None: - public_key_bytes, epoch_val = current_key - assert isinstance(public_key_bytes, bytes), "Public key should be bytes" - assert len(public_key_bytes) == 1184, ( - f"ML-KEM-768 public key should be 1184 bytes, got {len(public_key_bytes)}" - ) - assert isinstance(epoch_val, int), "Epoch should be int" - logging.debug( - f"CurrentKey found: epoch={epoch_val}, key_size={len(public_key_bytes)}" - ) - - # Query NextKey - may be None if no key announced yet - next_key = subtensor.mev_shield.get_mev_shield_next_key() - if next_key is not None: - public_key_bytes, epoch_val = next_key - assert isinstance(public_key_bytes, bytes), "Public key should be bytes" - assert len(public_key_bytes) == 1184, ( - f"ML-KEM-768 public key should be 1184 bytes, got {len(public_key_bytes)}" - ) - assert isinstance(epoch_val, int), "Epoch should be int" - logging.debug( - f"NextKey found: epoch={epoch_val}, key_size={len(public_key_bytes)}" - ) - - # Query all submissions - should always return a dict - all_submissions = subtensor.mev_shield.get_mev_shield_submission() - assert isinstance(all_submissions, dict), ( - f"Should return dict of all submissions, got {type(all_submissions)}" - ) - logging.debug(f"Found {len(all_submissions)} submission(s) in storage") - +def test_mev_shield_happy_path( + subtensor, alice_wallet, bob_wallet, charlie_wallet, dave_wallet +): + """Tests MEV Shield functionality with add_stake inner call. -def test_mev_shield_announce_next_key(subtensor, alice_wallet): - """Tests announcing next key as a validator. + This test verifies the complete MEV Shield flow: encrypting a transaction, submitting it, and verifying that + validators decrypt and execute it correctly. The test covers two scenarios: + - using default signer (wallet.coldkey) + - explicit signer keypair Steps: - 1. Generate ML-KEM-768 keypair using bittensor_drand - 2. Get current epoch - 3. Announce next key with next epoch - 4. Wait for next block - 5. Verify NextKey is set correctly - 6. Verify public_key size is 1184 bytes - 7. Verify epoch matches + - Register a subnet through Bob and activate it + - Register Charlie's neuron on the subnet + - Wait until the third epoch (MEV Shield logic requires at least 3 epochs with fast blocks) + - For each signer scenario (None/default and explicit dave_wallet.coldkey): + - Get the current stake before the transaction + - Create an add_stake_limit call for Charlie's hotkey + - Submit the call encrypted through MEV Shield using mev_submit_encrypted + - Wait for validators to decrypt and execute the transaction (3 blocks) + - Verify that the stake has increased after execution """ - # add founds to alice HK - response = add_balance_to_wallet_hk(subtensor, alice_wallet, 100) - assert response.success, response.message - # Set tempo to 100 blocks for root network (netuid=0) to ensure stable epoch timing - # MEV Shield epoch depends on root network tempo, so we need predictable epoch boundaries - TEMPO_TO_SET = 100 - root_sn = TestSubnet(subtensor, netuid=0) - tempo_response = root_sn.execute_one( - SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, 0, TEMPO_TO_SET) + bob_sn = TestSubnet(subtensor) + bob_sn.execute_steps( + [ + REGISTER_SUBNET(bob_wallet), + ACTIVATE_SUBNET(bob_wallet), + REGISTER_NEURON(charlie_wallet), + ] ) - assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" - # Wait for tempo transaction to be included in a block - # This ensures the tempo change is processed before we submit the announce transaction - current_block = subtensor.block - subtensor.wait_for_block(current_block + 1) - - # Generate ML-KEM-768 keypair using bittensor_drand - public_key_bytes = generate_mlkem768_keypair() - assert len(public_key_bytes) == 1184, ( - f"Generated key should be 1184 bytes, got {len(public_key_bytes)}" - ) - - # Get current epoch and prepare next epoch - # We use current_epoch + 1 because we're announcing the key for the NEXT epoch - current_epoch = subtensor.mev_shield.get_mev_shield_epoch() - next_epoch = current_epoch + 1 - - logging.info( - f"Announcing next key for epoch {next_epoch} (current epoch: {current_epoch})" - ) - - # Announce next key with explicit period to avoid transaction expiration - # Use a longer period (256 blocks) to ensure transaction doesn't expire during epoch transitions - response = announce_next_key_extrinsic( - subtensor=subtensor, - wallet=alice_wallet, - public_key=public_key_bytes, - epoch=next_epoch, - period=256, # Longer period to avoid "Transaction is outdated" errors - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - assert response.success, f"Failed to announce next key: {response.message}" - - # Verify that extrinsic was actually executed successfully (not just included) - assert response.extrinsic_receipt is not None, ( - "Extrinsic receipt should be available" + # MeV Res logic works not from before third epoch with fast blocks, so we need to wait for it + next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(bob_sn.netuid) + subtensor.wait_for_block( + next_epoch_start_block + subtensor.subnets.tempo(bob_sn.netuid) * 2 ) - assert response.extrinsic_receipt.is_success, ( - f"Extrinsic execution failed: {response.extrinsic_receipt.error_message}" - ) - - logging.info("Next key announced successfully, waiting for block...") - - # Wait for next block to ensure the announcement is processed - current_block = subtensor.block - subtensor.wait_for_block(current_block + 1) - - # Verify NextKey is set - next_key_result = subtensor.mev_shield.get_mev_shield_next_key() - assert next_key_result is not None, "NextKey should be set after announcement" - - pk_bytes, epoch = next_key_result - assert pk_bytes == public_key_bytes, "Public key should match announced key" - assert len(pk_bytes) == 1184, ( - f"Public key size should be 1184 bytes, got {len(pk_bytes)}" - ) - assert epoch == next_epoch, ( - f"Epoch should match announced epoch {next_epoch}, got {epoch}" - ) - - logging.info(f"NextKey verified: epoch={epoch}, key_size={len(pk_bytes)}") - -def test_mev_shield_submit_encrypted_full_flow(subtensor, bob_wallet, alice_wallet): - """Tests submitting an encrypted extrinsic with full verification. - - Steps: - 1. Ensure NextKey exists (announce if needed) - 2. Create a simple transfer call - 3. Submit encrypted extrinsic - 4. Wait for inclusion - 5. Verify submission exists in storage - 6. Verify commitment matches computed value - 7. Verify author matches wallet address - 8. Verify submitted_in block number - """ - - # add founds to alice, bob HK - for w in [alice_wallet, bob_wallet]: - response = add_balance_to_wallet_hk(subtensor, w, 100) - assert response.success, response.message - - # Ensure NextKey exists - if not, we need to announce it first using alice_wallet (validator) - next_key_result = subtensor.mev_shield.get_mev_shield_next_key() - if next_key_result is None: - # Set tempo to 100 blocks for root network (netuid=0) to ensure stable epoch timing - # MEV Shield epoch depends on root network tempo, so we need predictable epoch boundaries - TEMPO_TO_SET = 100 - root_sn = TestSubnet(subtensor, netuid=0) - tempo_response = root_sn.execute_one( - SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, 0, TEMPO_TO_SET) + for signer in [None, dave_wallet.coldkey]: + stake_before = subtensor.staking.get_stake( + coldkey_ss58=signer.ss58_address + if signer is not None + else alice_wallet.coldkey.ss58_address, + hotkey_ss58=charlie_wallet.hotkey.ss58_address, + netuid=bob_sn.netuid, ) - assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" + logging.console.info(f"Stake before: {stake_before}") - # Wait a few blocks after setting tempo to ensure state is synchronized - # This prevents "Transaction is outdated" errors by allowing the chain to process - # the tempo change and stabilize before we submit the announce transaction. - current_block = subtensor.block - subtensor.wait_for_block(current_block + 5) + subnet_price = subtensor.subnets.get_subnet_price(2) + limit_price = (subnet_price * 2).rao - # Generate and announce a key first using alice_wallet (which is a validator in localnet) - public_key_bytes = generate_mlkem768_keypair() - current_epoch = subtensor.mev_shield.get_mev_shield_epoch() - next_epoch = current_epoch + 1 - - logging.info( - f"NextKey not found, announcing key for epoch {next_epoch} using Alice wallet (validator)" + call = pallets.SubtensorModule(subtensor).add_stake_limit( + netuid=bob_sn.netuid, + hotkey=charlie_wallet.hotkey.ss58_address, + amount_staked=Balance.from_tao(5).rao, + allow_partial=True, + limit_price=limit_price, ) - # Announce key using validator wallet with explicit period to avoid transaction expiration - # Use a longer period (256 blocks) to ensure transaction doesn't expire during epoch transitions - announce_response = announce_next_key_extrinsic( - subtensor=subtensor, + response = subtensor.mev_shield.mev_submit_encrypted( wallet=alice_wallet, - public_key=public_key_bytes, - epoch=next_epoch, - period=256, # Longer period to avoid "Transaction is outdated" errors - wait_for_inclusion=True, - wait_for_finalization=True, + call=call, + signer_keypair=signer, + raise_error=True, ) - - assert announce_response.success, ( - f"Failed to announce next key: {announce_response.message}" - ) - assert announce_response.extrinsic_receipt is not None, ( - "Extrinsic receipt should be available" - ) - assert announce_response.extrinsic_receipt.is_success, ( - f"Extrinsic execution failed: {announce_response.extrinsic_receipt.error_message}" - ) - - # Wait for block to ensure key is set - current_block = subtensor.block - subtensor.wait_for_block(current_block + 1) - - # Verify NextKey is now set - next_key_result = subtensor.mev_shield.get_mev_shield_next_key() - assert next_key_result is not None, "NextKey should be set after announcement" - - pk_bytes, epoch = next_key_result - logging.info(f"Using NextKey with epoch {epoch} for encryption") - - # Create a simple transfer call - transfer_call = SubtensorModule(subtensor).transfer( - dest=bob_wallet.coldkey.ss58_address, - value=1000000, # 1 TAO in RAO - ) - - # Get nonce for commitment calculation - nonce = subtensor.substrate.get_account_next_index(bob_wallet.coldkey.ss58_address) - signer_bytes = bob_wallet.coldkey.public_key - nonce_bytes = nonce.to_bytes(4, "little") - scale_call_bytes = transfer_call.encode() - payload_core = signer_bytes + nonce_bytes + scale_call_bytes - - # Compute expected commitment (blake2_256 = blake2b with digest_size=32) - expected_commitment_hash = hashlib.blake2b(payload_core, digest_size=32).digest() - expected_commitment_hex = "0x" + expected_commitment_hash.hex() - - logging.info( - f"Submitting encrypted extrinsic with expected commitment: {expected_commitment_hex}" - ) - - # Submit encrypted extrinsic - response = submit_encrypted_extrinsic( - subtensor=subtensor, - wallet=bob_wallet, - call=transfer_call, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - assert response.success, f"Failed to submit encrypted extrinsic: {response.message}" - - logging.info("Encrypted extrinsic submitted successfully, waiting for block...") - - # Wait for block to be finalized - current_block = subtensor.block - subtensor.wait_for_block(current_block + 1) - - # Find submission by commitment - all_submissions = subtensor.mev_shield.get_mev_shield_submission() - submission = None - for sub_id, sub_data in all_submissions.items(): - if sub_data["commitment"] == expected_commitment_hex: - submission = sub_data - break - - assert submission is not None, ( - f"Submission not found in storage. Expected commitment: {expected_commitment_hex}" - ) - - assert submission["commitment"] == expected_commitment_hex, ( - f"Commitment mismatch: expected {expected_commitment_hex}, got {submission['commitment']}" - ) - assert submission["author"] == bob_wallet.coldkey.ss58_address, ( - f"Author mismatch: expected {bob_wallet.coldkey.ss58_address}, got {submission['author']}" - ) - assert isinstance(submission["submitted_in"], int), "submitted_in should be int" - assert submission["submitted_in"] > 0, "submitted_in should be positive" - assert isinstance(submission["ciphertext"], bytes), "ciphertext should be bytes" - assert len(submission["ciphertext"]) > 0, "ciphertext should not be empty" - - logging.info( - f"Submission verified: commitment={submission['commitment']}, " - f"author={submission['author']}, submitted_in={submission['submitted_in']}" - ) - - -def test_mev_shield_key_rotation(subtensor, alice_wallet): - """Tests key rotation from NextKey to CurrentKey. - - Steps: - 1. Generate ML-KEM-768 keypair - 2. Announce NextKey - 3. Verify NextKey is set - 4. Wait for next block (triggers rotation) - 5. Verify CurrentKey now contains the old NextKey - 6. Verify Epoch has been updated - """ - # add founds to alice HK - response = add_balance_to_wallet_hk(subtensor, alice_wallet, 100) - assert response.success, response.message - - # Set tempo to 100 blocks for root network (netuid=0) to ensure stable epoch timing - # MEV Shield epoch depends on root network tempo, so we need predictable epoch boundaries - TEMPO_TO_SET = 100 - root_sn = TestSubnet(subtensor, netuid=0) - tempo_response = root_sn.execute_one( - SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, 0, TEMPO_TO_SET) - ) - assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" - - # Wait a few blocks after setting tempo to ensure state is synchronized - # This prevents "Transaction is outdated" errors by allowing the chain to process - # the tempo change and stabilize before we submit the announce transaction. - current_block = subtensor.block - subtensor.wait_for_block(current_block + 5) - - # Generate ML-KEM-768 keypair using bittensor_drand - public_key_bytes = generate_mlkem768_keypair() - - # Get current epoch - current_epoch = subtensor.mev_shield.get_mev_shield_epoch() - next_epoch = current_epoch + 1 - - logging.info(f"Testing key rotation: announcing NextKey for epoch {next_epoch}") - - # Announce NextKey with explicit period to avoid transaction expiration - # Use a longer period (256 blocks) to ensure transaction doesn't expire during epoch transitions - response = announce_next_key_extrinsic( - subtensor=subtensor, - wallet=alice_wallet, - public_key=public_key_bytes, - epoch=next_epoch, - period=256, # Longer period to avoid "Transaction is outdated" errors - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - assert response.success, f"Failed to announce next key: {response.message}" - - # Verify that extrinsic was actually executed successfully (not just included) - assert response.extrinsic_receipt is not None, ( - "Extrinsic receipt should be available" - ) - assert response.extrinsic_receipt.is_success, ( - f"Extrinsic execution failed: {response.extrinsic_receipt.error_message}" - ) - - # Verify NextKey is set - next_key_before = subtensor.mev_shield.get_mev_shield_next_key() - assert next_key_before is not None, "NextKey should be set after announcement" - pk_bytes_before, epoch_before = next_key_before - assert pk_bytes_before == public_key_bytes, ( - "NextKey public key should match announced key" - ) - assert epoch_before == next_epoch, ( - f"NextKey epoch should be {next_epoch}, got {epoch_before}" - ) - - logging.info(f"NextKey set: epoch={epoch_before}, waiting for rotation...") - - # Wait for next block (this triggers rotation: NextKey -> CurrentKey) - current_block = subtensor.block - subtensor.wait_for_block(current_block + 1) - - # Verify CurrentKey now contains the old NextKey - current_key_after = subtensor.mev_shield.get_mev_shield_current_key() - assert current_key_after is not None, "CurrentKey should be set after rotation" - - pk_bytes_after, epoch_after = current_key_after - assert pk_bytes_after == public_key_bytes, ( - "CurrentKey should contain the old NextKey" - ) - assert epoch_after == next_epoch, ( - f"CurrentKey epoch should be {next_epoch}, got {epoch_after}" - ) - - # Verify Epoch has been updated - new_epoch = subtensor.mev_shield.get_mev_shield_epoch() - assert new_epoch == next_epoch, ( - f"Epoch should be updated to {next_epoch}, got {new_epoch}" - ) - - logging.info( - f"Key rotation verified: CurrentKey epoch={epoch_after}, global epoch={new_epoch}" - ) - - -def test_mev_shield_commitment_verification(subtensor, bob_wallet, alice_wallet): - """Tests commitment verification by recomputing it from payload. - - Steps: - 1. Ensure NextKey exists - 2. Create a call - 3. Submit encrypted extrinsic - 4. Get submission from storage - 5. Recompute payload_core from call parameters - 6. Recompute commitment (blake2b) - 7. Verify commitment from storage matches recomputed value - """ - - # add founds to alice, bob HK - for w in [alice_wallet, bob_wallet]: - response = add_balance_to_wallet_hk(subtensor, w, 100) assert response.success, response.message - - # Ensure NextKey exists - if not, we need to announce it first using alice_wallet (validator) - next_key_result = subtensor.mev_shield.get_mev_shield_next_key() - if next_key_result is None: - # Set tempo to 100 blocks for root network (netuid=0) to ensure stable epoch timing - # MEV Shield epoch depends on root network tempo, so we need predictable epoch boundaries - TEMPO_TO_SET = 100 - root_sn = TestSubnet(subtensor, netuid=0) - tempo_response = root_sn.execute_one( - SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, 0, TEMPO_TO_SET) + assert response.data.get("revealed_extrinsic_receipt") is not None, ( + "No revealed extrinsic receipt." ) - assert tempo_response.success, f"Failed to set tempo: {tempo_response.message}" - - # Wait a few blocks after setting tempo to ensure state is synchronized - # This prevents "Transaction is outdated" errors by allowing the chain to process - # the tempo change and stabilize before we submit the announce transaction. - current_block = subtensor.block - subtensor.wait_for_block(current_block + 5) - - # Generate and announce a key first using alice_wallet (which is a validator in localnet) - public_key_bytes = generate_mlkem768_keypair() - current_epoch = subtensor.mev_shield.get_mev_shield_epoch() - next_epoch = current_epoch + 1 - logging.info( - f"NextKey not found, announcing key for epoch {next_epoch} using Alice wallet (validator)" - ) - - # Announce key using validator wallet with explicit period to avoid transaction expiration - # Use a longer period (256 blocks) to ensure transaction doesn't expire during epoch transitions - announce_response = announce_next_key_extrinsic( - subtensor=subtensor, - wallet=alice_wallet, - public_key=public_key_bytes, - epoch=next_epoch, - period=256, # Longer period to avoid "Transaction is outdated" errors - wait_for_inclusion=True, - wait_for_finalization=True, - ) + subtensor.wait_for_block(subtensor.block + 3) - assert announce_response.success, ( - f"Failed to announce next key: {announce_response.message}" + stake_after = subtensor.staking.get_stake( + coldkey_ss58=signer.ss58_address + if signer is not None + else alice_wallet.coldkey.ss58_address, + hotkey_ss58=charlie_wallet.hotkey.ss58_address, + netuid=bob_sn.netuid, ) - assert announce_response.extrinsic_receipt is not None, ( - "Extrinsic receipt should be available" - ) - assert announce_response.extrinsic_receipt.is_success, ( - f"Extrinsic execution failed: {announce_response.extrinsic_receipt.error_message}" - ) - - # Wait for block to ensure key is set - current_block = subtensor.block - subtensor.wait_for_block(current_block + 1) - - # Verify NextKey is now set - next_key_result = subtensor.mev_shield.get_mev_shield_next_key() - assert next_key_result is not None, "NextKey should be set after announcement" - - # Create a simple transfer call - transfer_call = SubtensorModule(subtensor).transfer( - dest=bob_wallet.coldkey.ss58_address, - value=2000000, # 2 TAO in RAO - ) - - # Get nonce that will be used - nonce = subtensor.substrate.get_account_next_index(bob_wallet.coldkey.ss58_address) - - # Compute payload_core: signer (32B) + nonce (u32 LE, 4B) + SCALE(call) - signer_bytes = bob_wallet.coldkey.public_key - nonce_bytes = nonce.to_bytes(4, "little") - scale_call_bytes = transfer_call.encode() - payload_core = signer_bytes + nonce_bytes + scale_call_bytes - - # Compute expected commitment (blake2_256 = blake2b with digest_size=32) - expected_commitment_hash = hashlib.blake2b(payload_core, digest_size=32).digest() - expected_commitment_hex = "0x" + expected_commitment_hash.hex() - - logging.info( - f"Submitting encrypted extrinsic, expected commitment: {expected_commitment_hex}" - ) - - # Submit encrypted extrinsic - response = submit_encrypted_extrinsic( - subtensor=subtensor, - wallet=bob_wallet, - call=transfer_call, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - assert response.success, f"Failed to submit encrypted extrinsic: {response.message}" - - # Wait for block - current_block = subtensor.block - subtensor.wait_for_block(current_block + 1) - - # Find submission by commitment - all_submissions = subtensor.mev_shield.get_mev_shield_submission() - submission = None - for sub_id, sub_data in all_submissions.items(): - if sub_data["commitment"] == expected_commitment_hex: - submission = sub_data - break - - assert submission is not None, ( - f"Submission not found with commitment {expected_commitment_hex}" - ) - - # Verify commitment matches - storage_commitment = submission["commitment"] - assert storage_commitment == expected_commitment_hex, ( - f"Commitment mismatch: storage has {storage_commitment}, " - f"expected {expected_commitment_hex}" - ) - - # Recompute commitment to double-check - recomputed_commitment_hash = hashlib.blake2b(payload_core, digest_size=32).digest() - recomputed_commitment_hex = "0x" + recomputed_commitment_hash.hex() - - assert recomputed_commitment_hex == expected_commitment_hex, ( - "Recomputed commitment should match expected" - ) - assert storage_commitment == recomputed_commitment_hex, ( - f"Storage commitment {storage_commitment} should match recomputed {recomputed_commitment_hex}" - ) - - logging.info(f"Commitment verification passed: {storage_commitment}") + logging.console.info(f"Stake after: {stake_after}") + assert stake_after > stake_before From 611ebbbc05bdb0e7ca83bd45e4289a06676d703b Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:34:24 -0800 Subject: [PATCH 16/67] ruff --- bittensor/core/extrinsics/mev_shield.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index 50888ddd90..d4d78f8265 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -5,7 +5,10 @@ from async_substrate_interface import ExtrinsicReceipt from bittensor.core.extrinsics.pallets import MevShield -from bittensor.core.extrinsics.utils import get_event_data, get_mev_commitment_and_ciphertext +from bittensor.core.extrinsics.utils import ( + get_event_data, + get_mev_commitment_and_ciphertext, +) from bittensor.core.types import ExtrinsicResponse from bittensor.utils.btlogging import logging From ea9ba62c25146f46bc619404e5396390915fb429 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:58:43 -0800 Subject: [PATCH 17/67] update SubtensorApi --- bittensor/extras/subtensor_api/mev_shield.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bittensor/extras/subtensor_api/mev_shield.py b/bittensor/extras/subtensor_api/mev_shield.py index 75e97ddf50..8484e1a766 100644 --- a/bittensor/extras/subtensor_api/mev_shield.py +++ b/bittensor/extras/subtensor_api/mev_shield.py @@ -12,6 +12,7 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_mev_shield_current_key = subtensor.get_mev_shield_current_key self.get_mev_shield_next_key = subtensor.get_mev_shield_next_key self.get_mev_shield_submission = subtensor.get_mev_shield_submission + self.get_mev_shield_submissions = subtensor.get_mev_shield_submissions # Extrinsics self.mev_submit_encrypted = subtensor.mev_submit_encrypted From 7561b703bb4ea0369848e1d3de17c2c35a824562 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:59:09 -0800 Subject: [PATCH 18/67] add async extrinsic --- .../core/extrinsics/asyncex/mev_shield.py | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 bittensor/core/extrinsics/asyncex/mev_shield.py diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py new file mode 100644 index 0000000000..0fc9118749 --- /dev/null +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -0,0 +1,233 @@ +"""Module provides async MEV Shield extrinsics.""" + +from typing import TYPE_CHECKING, Optional + +from async_substrate_interface import AsyncExtrinsicReceipt + +from bittensor.core.extrinsics.pallets import MevShield +from bittensor.core.extrinsics.utils import ( + get_event_data, + get_mev_commitment_and_ciphertext, +) +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils.btlogging import logging + +if TYPE_CHECKING: + from bittensor.core.async_subtensor import AsyncSubtensor + from bittensor_wallet import Wallet, Keypair + from scalecodec.types import GenericCall + + +async def find_revealed_extrinsic( + subtensor: "AsyncSubtensor", + signer_ss58: str, + event_id: str, + event_hash_id: str, + start_block_hash: str, + blocks_ahead: int = 5, +) -> Optional["AsyncExtrinsicReceipt"]: + """ + Searches for an extrinsic containing a specific MEV Shield event in subsequent blocks. + + This function iterates through blocks starting from the specified block hash and searches for extrinsics that + contain a MEV Shield event (DecryptedExecuted or DecryptedRejected) matching the provided wrapper_id and signer. It + checks each extrinsic's triggered events to find the matching event. + + Parameters: + subtensor: The Subtensor instance used for blockchain queries. + signer_ss58: The SS58 address of the signer account. Used to verify that the event belongs to the correct + transaction (matches the "signer" attribute in the event). + event_id: The event identifier to search for. Typically "DecryptedExecuted" or "DecryptedRejected" for MEV + Shield transactions. + event_hash_id: The wrapper_id (hash of (author, commitment, ciphertext)) to match. This uniquely identifies a + specific MEV Shield submission. + start_block_hash: The hash of the block where the search should begin. Usually the block where submit_encrypted + was included. + blocks_ahead: Maximum number of blocks to search ahead from the start block. Defaults to 5 blocks. The function + will check blocks from start_block + 1 to start_block + blocks_ahead (the start block itself is not checked, + as execute_revealed will be in subsequent blocks). + + Returns: + The ExtrinsicReceipt object for the extrinsic containing the matching event, or None if the event is not found + within the specified block range. + """ + start_block_number = await subtensor.substrate.get_block_number(start_block_hash) + + for offset in range(1, blocks_ahead + 1): + current_block_number = start_block_number + offset + + try: + current_block_hash = subtensor.substrate.get_block_hash( + current_block_number + ) + extrinsics = subtensor.substrate.get_extrinsics(current_block_hash) + except Exception as e: + logging.debug( + f"Error getting extrinsics for block `{current_block_number}`: {e}" + ) + continue + + for idx, e in enumerate(extrinsics): + extrinsic_ = AsyncExtrinsicReceipt( + substrate=subtensor.substrate, + extrinsic_hash=e.extrinsic_hash, + block_hash=current_block_hash, + extrinsic_idx=idx, + ) + + if triggered_events := extrinsic_.triggered_events: + event_data = get_event_data(triggered_events, event_id) + if ( + event_data + and event_hash_id == event_data["id"] + and signer_ss58 == event_data["signer"] + ): + return extrinsic_ + + await subtensor.wait_for_block() + + return None + + +async def submit_encrypted_extrinsic( + subtensor: "AsyncSubtensor", + wallet: "Wallet", + call: "GenericCall", + signer_keypair: Optional["Keypair"] = None, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + wait_for_revealed_execution: Optional[int] = 5, +) -> ExtrinsicResponse: + """ + Submits an encrypted extrinsic to the MEV Shield pallet. + + This function encrypts a call using ML-KEM-768 + XChaCha20Poly1305 and submits it to the MevShield pallet. The + extrinsic remains encrypted in the transaction pool until it is included in a block and decrypted by validators. + + Parameters: + subtensor: The Subtensor client instance used for blockchain interaction. + wallet: The wallet used to sign the extrinsic (must be unlocked, coldkey will be used for signing). + call: The GenericCall object to encrypt and submit. + signer_keypair: The Keypair object used for signing the inner call. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Maximum number of blocks to wait for the DecryptedExecuted event, indicating that + node validators have successfully decrypted and executed the inner call via execute_revealed. If None, the + function will not wait for revealed execution. If an integer (default: 5), the function will poll up to that + many blocks after inclusion, checking for the DecryptedExecuted event matching this submission's commitment. + The function returns immediately if the event is found before the block limit is reached. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Raises: + ValueError: If NextKey is not available in storage or encryption fails. + SubstrateRequestException: If the extrinsic fails to be submitted or included. + + Note: + The encryption uses the public key from NextKey storage, which rotates every block. The payload structure is: + payload_core = signer_bytes (32B) + key_hash (32B Blake2-256 hash of NextKey) + SCALE(call) + plaintext = payload_core + b"\\x01" + signature (64B for sr25519) + commitment = blake2_256(payload_core) + + The key_hash binds the transaction to the key epoch at submission time and replaces nonce-based replay + protection. + """ + try: + if not ( + unlocked := ExtrinsicResponse.unlock_wallet( + wallet, raise_error, unlock_type="both" + ) + ).success: + return unlocked + + # Use wallet.coldkey as default signer if signer_keypair is not provided + if signer_keypair is None: + signer_keypair = wallet.coldkey + + ml_kem_768_public_key = await subtensor.get_mev_shield_next_key() + if ml_kem_768_public_key is None: + # Fallback to CurrentKey if NextKey is not available + current_key_result = await subtensor.get_mev_shield_current_key() + if current_key_result is None: + return ExtrinsicResponse.from_exception( + raise_error=raise_error, + error=ValueError("MEV Shield NextKey not available in storage."), + ) + ml_kem_768_public_key = current_key_result + + genesis_hash = await subtensor.get_block_hash(block=0) + + mev_commitment, mev_ciphertext, payload_core, signature = ( + get_mev_commitment_and_ciphertext( + call=call, + signer_keypair=signer_keypair, + genesis_hash=genesis_hash, + ml_kem_768_public_key=ml_kem_768_public_key, + ) + ) + + extrinsic_call = MevShield(subtensor).submit_encrypted( + commitment=mev_commitment, + ciphertext=mev_ciphertext, + ) + + response = await subtensor.sign_and_send_extrinsic( + wallet=wallet, + call=extrinsic_call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if response.success: + response.data = { + "commitment": mev_commitment, + "ciphertext": mev_ciphertext, + "ml_kem_768_public_key": ml_kem_768_public_key, + "payload_core": payload_core, + "signature": signature, + "submitting_id": extrinsic_call.call_hash, + } + if wait_for_revealed_execution is not None and isinstance( + wait_for_revealed_execution, int + ): + triggered_events = await response.extrinsic_receipt.triggered_events + event_hash_id = get_event_data(triggered_events, "EncryptedSubmitted")[ + "id" + ] + + revealed_extrinsic_receipt = await find_revealed_extrinsic( + subtensor=subtensor, + signer_ss58=signer_keypair.ss58_address, + event_id="DecryptedExecuted", + event_hash_id=event_hash_id, + start_block_hash=response.extrinsic_receipt.block_hash, + blocks_ahead=wait_for_revealed_execution, + ) + if revealed_extrinsic_receipt: + response.data.update( + {"revealed_extrinsic_receipt": revealed_extrinsic_receipt} + ) + else: + response.success = False + response.error = RuntimeError(f"DecryptedExecuted event not found.") + return response.from_exception( + raise_error=raise_error, error=response.error + ) + + logging.debug("[green]Encrypted extrinsic submitted successfully.[/green]") + else: + logging.error(f"[red]{response.message}[/red]") + + return response + + except Exception as error: + return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) From 015eb62123cf2ed2ff20a397114f7696007a07a9 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:59:28 -0800 Subject: [PATCH 19/67] add async subtensor methods --- bittensor/core/async_subtensor.py | 258 ++++++++++++++++++++++++++++++ bittensor/core/subtensor.py | 18 ++- 2 files changed, 275 insertions(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 1d8c984649..7c451e1414 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -67,6 +67,7 @@ remove_liquidity_extrinsic, toggle_user_liquidity_extrinsic, ) +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.asyncex.move_stake import ( transfer_stake_extrinsic, swap_stake_extrinsic, @@ -2949,6 +2950,212 @@ async def get_metagraph_info( return MetagraphInfo.from_dict(query.value) + async def get_mev_shield_current_key( + self, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, + ) -> Optional[bytes]: + """ + Retrieves the CurrentKey from the MevShield pallet storage. + + The CurrentKey contains the ML-KEM-768 public key that is currently being used for encryption in this block. + This key is rotated from NextKey at the beginning of each block. + + Parameters: + block: The blockchain block number for the query. + block_hash: The hash of the block to retrieve the stake from. Do not specify if using block or reuse_block. + reuse_block: Whether to use the last-used block. Do not set if using block_hash or block. + + Returns: + The ML-KEM-768 public key as bytes (1184 bytes for ML-KEM-768) + + Note: + If CurrentKey is not set (None in storage), this function returns None. This can happen if no validator has + announced a key yet. + """ + block_hash = await self.determine_block_hash(block, block_hash, reuse_block) + query = await self.substrate.query( + module="MevShield", + storage_function="CurrentKey", + block_hash=block_hash, + ) + + if query is None: + return None + + public_key_bytes = bytes(next(iter(query))) + + # Validate public_key size for ML-KEM-768 (must be exactly 1184 bytes) + MLKEM768_PUBLIC_KEY_SIZE = 1184 + if len(public_key_bytes) != MLKEM768_PUBLIC_KEY_SIZE: + raise ValueError( + f"Invalid ML-KEM-768 public key size: {len(public_key_bytes)} bytes. " + f"Expected exactly {MLKEM768_PUBLIC_KEY_SIZE} bytes." + ) + + return public_key_bytes + + async def get_mev_shield_next_key( + self, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, + ) -> Optional[bytes]: + """ + Retrieves the NextKey from the MevShield pallet storage. + + The NextKey contains the ML-KEM-768 public key that will be used for encryption in the next block. This key is + rotated from NextKey to CurrentKey at the beginning of each block. + + Parameters: + block: The blockchain block number for the query. + block_hash: The hash of the block to retrieve the stake from. Do not specify if using block or reuse_block. + reuse_block: Whether to use the last-used block. Do not set if using block_hash or block. + + Returns: + The ML-KEM-768 public key as bytes (1184 bytes for ML-KEM-768) + + Note: + If NextKey is not set (None in storage), this function returns None. This can happen if no validator has + announced the next key yet. + """ + block_hash = await self.determine_block_hash(block, block_hash, reuse_block) + query = await self.substrate.query( + module="MevShield", + storage_function="NextKey", + block_hash=block_hash, + ) + + if query is None: + return None + + public_key_bytes = bytes(next(iter(query))) + + # Validate public_key size for ML-KEM-768 (must be exactly 1184 bytes) + MLKEM768_PUBLIC_KEY_SIZE = 1184 + if len(public_key_bytes) != MLKEM768_PUBLIC_KEY_SIZE: + raise ValueError( + f"Invalid ML-KEM-768 public key size: {len(public_key_bytes)} bytes. " + f"Expected exactly {MLKEM768_PUBLIC_KEY_SIZE} bytes." + ) + + return public_key_bytes + + async def get_mev_shield_submission( + self, + submission_id: str, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, + ) -> Optional[dict[str, str | int | bytes]]: + """ + Retrieves Submission from the MevShield pallet storage. + + If submission_id is provided, returns a single submission. If submission_id is None, returns all submissions from + the storage map. + + Parameters: + submission_id: The hash ID of the submission. Can be a hex string with "0x" prefix or bytes. If None, + returns all submissions. + block: The blockchain block number for the query. + block_hash: The hash of the block to retrieve the stake from. Do not specify if using block or reuse_block. + reuse_block: Whether to use the last-used block. Do not set if using block_hash or block. + + Returns: + If submission_id is provided: A dictionary containing the submission data if found, None otherwise. The + dictionary contains: + - author: The SS58 address of the account that submitted the encrypted extrinsic + - commitment: The blake2_256 hash of the payload_core (as hex string with "0x" prefix) + - ciphertext: The encrypted blob as bytes (format: [u16 kem_len][kem_ct][nonce24][aead_ct]) + - submitted_in: The block number when the submission was created + + If submission_id is None: A dictionary mapping submission IDs (as hex strings) to submission dictionaries. + + Note: + If a specific submission does not exist in storage, this function returns None. If querying all submissions + and none exist, returns an empty dictionary. + """ + block_hash = await self.determine_block_hash(block, block_hash, reuse_block) + submission_id = ( + submission_id[2:] if submission_id.startswith("0x") else submission_id + ) + submission_id_bytes = bytes.fromhex(submission_id) + + query = await self.substrate.query( + module="MevShield", + storage_function="Submissions", + params=[submission_id_bytes], + block_hash=block_hash, + ) + + if query is None or not isinstance(query, dict): + return None + + autor = decode_account_id(query.get("author")) + commitment = bytes(query.get("commitment")[0]) + ciphertext = bytes(query.get("ciphertext")[0]) + submitted_in = query.get("submitted_in") + + return { + "author": autor, + "commitment": commitment, + "ciphertext": ciphertext, + "submitted_in": submitted_in, + } + + async def get_mev_shield_submissions( + self, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, + ) -> Optional[dict[str, dict[str, str | int]]]: + """ + Retrieves all encrypted submissions from the MevShield pallet storage. + + This function queries the MevShield.Submissions storage map and returns all pending encrypted submissions that + have been submitted via submit_encrypted but not yet executed via execute_revealed. + + Parameters: + block: The blockchain block number for the query. If None, uses the current block. + block_hash: The hash of the block to retrieve the submissions from. Do not specify if using block or reuse_block. + reuse_block: Whether to use the last-used block. Do not set if using block_hash or block. + + Returns: + A dictionary mapping wrapper_id (as hex string with "0x" prefix) to submission data dictionaries. Each + submission dictionary contains: + - author: The SS58 address of the account that submitted the encrypted extrinsic + - commitment: The blake2_256 hash of the payload_core as bytes (32 bytes) + - ciphertext: The encrypted blob as bytes (format: [u16 kem_len][kem_ct][nonce24][aead_ct]) + - submitted_in: The block number when the submission was created + + Returns None if no submissions exist in storage at the specified block. + + Note: + Submissions are automatically pruned after KEY_EPOCH_HISTORY blocks (100 blocks) by the pallet's + on_initialize hook. Only submissions that have been submitted but not yet executed will be present in + storage. + """ + block_hash = await self.determine_block_hash(block, block_hash, reuse_block) + query = await self.substrate.query_map( + module="MevShield", + storage_function="Submissions", + block_hash=block_hash, + ) + + result = {} + async for q in query: + key, value = q + value = value.value + result["0x" + bytes(key[0]).hex()] = { + "author": decode_account_id(value.get("author")), + "commitment": bytes(value.get("commitment")[0]), + "ciphertext": bytes(value.get("ciphertext")[0]), + "submitted_in": value.get("submitted_in"), + } + + return result if result else None + async def get_minimum_required_stake(self): """ Returns the minimum required stake for nominators in the Subtensor network. @@ -6304,6 +6511,57 @@ async def kill_pure_proxy( wait_for_finalization=wait_for_finalization, ) + async def mev_submit_encrypted( + self, + wallet: "Wallet", + call: "GenericCall", + signer_keypair: Optional["Keypair"] = None, + period: Optional[int] = DEFAULT_PERIOD, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + ) -> ExtrinsicResponse: + """ + Submits an encrypted extrinsic to the MEV Shield pallet. + + This function encrypts a call using ML-KEM-768 + XChaCha20Poly1305 and submits it to the MevShield pallet. The + extrinsic remains encrypted in the transaction pool until it is included in a block and decrypted by validators. + + Parameters: + wallet: The wallet used to sign the extrinsic (must be unlocked, coldkey will be used for signing). + call: The GenericCall object to encrypt and submit. + signer_keypair: The keypair used to sign the inner call. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Raises: + ValueError: If NextKey is not available in storage or encryption fails. + SubstrateRequestException: If the extrinsic fails to be submitted or included. + + Note: + The encryption uses the public key from NextKey storage, which rotates every block. The payload structure is: + payload_core = signer_bytes (32B) + nonce (u32 LE, 4B) + SCALE(call) + plaintext = payload_core + b"\\x01" + signature (64B for sr25519) + commitment = blake2_256(payload_core) + """ + return await submit_encrypted_extrinsic( + subtensor=self, + wallet=wallet, + call=call, + signer_keypair=signer_keypair, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + async def modify_liquidity( self, wallet: "Wallet", diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index c06f838640..e62d3a2298 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2377,12 +2377,28 @@ def get_mev_shield_submissions( block: Optional[int] = None, ) -> Optional[dict[str, dict[str, str | int]]]: """ - Retrieves Submission(s) from the MevShield pallet storage. + Retrieves all encrypted submissions from the MevShield pallet storage. + + This function queries the MevShield.Submissions storage map and returns all pending encrypted submissions that + have been submitted via submit_encrypted but not yet executed via execute_revealed. Parameters: + block: The blockchain block number for the query. If None, uses the current block. Returns: + A dictionary mapping wrapper_id (as hex string with "0x" prefix) to submission data dictionaries. Each + submission dictionary contains: + - author: The SS58 address of the account that submitted the encrypted extrinsic + - commitment: The blake2_256 hash of the payload_core as bytes (32 bytes) + - ciphertext: The encrypted blob as bytes (format: [u16 kem_len][kem_ct][nonce24][aead_ct]) + - submitted_in: The block number when the submission was created + + Returns None if no submissions exist in storage at the specified block. + Note: + Submissions are automatically pruned after KEY_EPOCH_HISTORY blocks (100 blocks) by the pallet's + on_initialize hook. Only submissions that have been submitted but not yet executed will be present in + storage. """ block_hash = self.determine_block_hash(block=block) query = self.substrate.query_map( From 036e04489d7da12523d9e0f508b8f7ae6fefa2dc Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:59:41 -0800 Subject: [PATCH 20/67] fix subtensor_api test --- tests/unit_tests/test_subtensor_api.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index 7985959aba..606bd76ca3 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -41,6 +41,9 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): stakes_methods = [m for m in dir(subtensor_api.staking) if not m.startswith("_")] subnets_methods = [m for m in dir(subtensor_api.subnets) if not m.startswith("_")] wallets_methods = [m for m in dir(subtensor_api.wallets) if not m.startswith("_")] + mev_shield_methods = [ + m for m in dir(subtensor_api.mev_shield) if not m.startswith("_") + ] all_subtensor_api_methods = ( subtensor_api_methods @@ -56,6 +59,7 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): + stakes_methods + subnets_methods + wallets_methods + + mev_shield_methods ) # Assertions From 11b0344510568a35cdddd8144eb1b6d5c0047bcc Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 20:59:49 -0800 Subject: [PATCH 21/67] add async e2e test --- tests/e2e_tests/test_mev_shield.py | 86 +++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_mev_shield.py b/tests/e2e_tests/test_mev_shield.py index 0ec530fba4..239fd54e33 100644 --- a/tests/e2e_tests/test_mev_shield.py +++ b/tests/e2e_tests/test_mev_shield.py @@ -83,7 +83,7 @@ def test_mev_shield_happy_path( "No revealed extrinsic receipt." ) - subtensor.wait_for_block(subtensor.block + 3) + # subtensor.wait_for_block(subtensor.block + 3) stake_after = subtensor.staking.get_stake( coldkey_ss58=signer.ss58_address @@ -94,3 +94,87 @@ def test_mev_shield_happy_path( ) logging.console.info(f"Stake after: {stake_after}") assert stake_after > stake_before + + +async def test_mev_shield_happy_path_async( + async_subtensor, alice_wallet, bob_wallet, charlie_wallet, dave_wallet +): + """Async tests MEV Shield functionality with add_stake inner call. + + This test verifies the complete MEV Shield flow: encrypting a transaction, submitting it, and verifying that + validators decrypt and execute it correctly. The test covers two scenarios: + - using default signer (wallet.coldkey) + - explicit signer keypair + + Steps: + - Register a subnet through Bob and activate it + - Register Charlie's neuron on the subnet + - Wait until the third epoch (MEV Shield logic requires at least 3 epochs with fast blocks) + - For each signer scenario (None/default and explicit dave_wallet.coldkey): + - Get the current stake before the transaction + - Create an add_stake_limit call for Charlie's hotkey + - Submit the call encrypted through MEV Shield using mev_submit_encrypted + - Wait for validators to decrypt and execute the transaction (3 blocks) + - Verify that the stake has increased after execution + """ + + bob_sn = TestSubnet(async_subtensor) + await bob_sn.async_execute_steps( + [ + REGISTER_SUBNET(bob_wallet), + ACTIVATE_SUBNET(bob_wallet), + REGISTER_NEURON(charlie_wallet), + ] + ) + + # MeV Res logic works not from before third epoch with fast blocks, so we need to wait for it + next_epoch_start_block = await async_subtensor.subnets.get_next_epoch_start_block( + bob_sn.netuid + ) + await async_subtensor.wait_for_block( + next_epoch_start_block + await async_subtensor.subnets.tempo(bob_sn.netuid) * 2 + ) + + for signer in [None, dave_wallet.coldkey]: + stake_before = await async_subtensor.staking.get_stake( + coldkey_ss58=signer.ss58_address + if signer is not None + else alice_wallet.coldkey.ss58_address, + hotkey_ss58=charlie_wallet.hotkey.ss58_address, + netuid=bob_sn.netuid, + ) + logging.console.info(f"Stake before: {stake_before}") + + subnet_price = await async_subtensor.subnets.get_subnet_price(2) + limit_price = (subnet_price * 2).rao + + call = pallets.SubtensorModule(async_subtensor).add_stake_limit( + netuid=bob_sn.netuid, + hotkey=charlie_wallet.hotkey.ss58_address, + amount_staked=Balance.from_tao(5).rao, + allow_partial=True, + limit_price=limit_price, + ) + + response = await async_subtensor.mev_shield.mev_submit_encrypted( + wallet=alice_wallet, + call=call, + signer_keypair=signer, + raise_error=True, + ) + assert response.success, response.message + assert response.data.get("revealed_extrinsic_receipt") is not None, ( + "No revealed extrinsic receipt." + ) + + # await async_subtensor.wait_for_block(await async_subtensor.block + 3) + + stake_after = await async_subtensor.staking.get_stake( + coldkey_ss58=signer.ss58_address + if signer is not None + else alice_wallet.coldkey.ss58_address, + hotkey_ss58=charlie_wallet.hotkey.ss58_address, + netuid=bob_sn.netuid, + ) + logging.console.info(f"Stake after: {stake_after}") + assert stake_after > stake_before From 7dceefbe1401c51b98915e3dc60a14b7b59c6eee Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 22:04:27 -0800 Subject: [PATCH 22/67] fixes --- bittensor/core/extrinsics/asyncex/mev_shield.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index 0fc9118749..b6710ab506 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -57,10 +57,10 @@ async def find_revealed_extrinsic( current_block_number = start_block_number + offset try: - current_block_hash = subtensor.substrate.get_block_hash( + current_block_hash = await subtensor.substrate.get_block_hash( current_block_number ) - extrinsics = subtensor.substrate.get_extrinsics(current_block_hash) + extrinsics = await subtensor.substrate.get_extrinsics(current_block_hash) except Exception as e: logging.debug( f"Error getting extrinsics for block `{current_block_number}`: {e}" @@ -75,7 +75,7 @@ async def find_revealed_extrinsic( extrinsic_idx=idx, ) - if triggered_events := extrinsic_.triggered_events: + if triggered_events := await extrinsic_.triggered_events: event_data = get_event_data(triggered_events, event_id) if ( event_data @@ -173,7 +173,7 @@ async def submit_encrypted_extrinsic( ) ) - extrinsic_call = MevShield(subtensor).submit_encrypted( + extrinsic_call = await MevShield(subtensor).submit_encrypted( commitment=mev_commitment, ciphertext=mev_ciphertext, ) From 7a345c4d5f514e27a9053515e43956e0c2e7d439 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 22:04:54 -0800 Subject: [PATCH 23/67] add `with_mev_protection` flag to `add_stake` --- bittensor/core/async_subtensor.py | 5 +++ bittensor/core/extrinsics/asyncex/staking.py | 37 ++++++++++++++------ bittensor/core/extrinsics/staking.py | 37 ++++++++++++++------ bittensor/core/subtensor.py | 5 +++ 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 7c451e1414..7a08c878f8 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5810,6 +5810,7 @@ async def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + with_mev_protection: bool = False, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5831,6 +5832,9 @@ async def add_stake( exceed the price tolerance. If false, the entire stake fails if it would exceed the tolerance. rate_tolerance: The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price increase. Only used when safe_staking is True. + with_mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to + protect against front-running and MEV attacks. The transaction remains encrypted in the mempool until + validators decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5855,6 +5859,7 @@ async def add_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + with_mev_protection=with_mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index d687afd346..67209e64d6 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -4,6 +4,7 @@ from async_substrate_interface.errors import SubstrateRequestException from bittensor.core.errors import BalanceTypeError +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule from bittensor.core.extrinsics.utils import get_old_stakes from bittensor.core.types import ExtrinsicResponse, UIDs @@ -25,6 +26,7 @@ async def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + with_mev_protection: bool = False, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -44,6 +46,9 @@ async def add_stake_extrinsic( safe_staking: If True, enables price safety checks. allow_partial_stake: If True, allows partial unstaking if price tolerance exceeded. rate_tolerance: Maximum allowed price increase percentage (0.005 = 0.5%). + with_mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to + protect against front-running and MEV attacks. The transaction remains encrypted in the mempool until + validators decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -134,16 +139,28 @@ async def add_stake_extrinsic( ) block_hash_before = await subtensor.get_block_hash() - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - nonce_key="coldkeypub", - use_nonce=True, - period=period, - raise_error=raise_error, - ) + if with_mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + nonce_key="coldkeypub", + use_nonce=True, + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = await subtensor.sim_swap( origin_netuid=0, diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 210b80d648..7118f57caf 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -3,6 +3,7 @@ from async_substrate_interface.errors import SubstrateRequestException from bittensor.core.errors import BalanceTypeError +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule from bittensor.core.extrinsics.utils import get_old_stakes from bittensor.core.types import ExtrinsicResponse, UIDs @@ -24,6 +25,7 @@ def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + with_mev_protection: bool = False, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -43,6 +45,9 @@ def add_stake_extrinsic( safe_staking: If True, enables price safety checks. allow_partial_stake: If True, allows partial unstaking if price tolerance exceeded. rate_tolerance: Maximum allowed price increase percentage (0.005 = 0.5%). + with_mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to + protect against front-running and MEV attacks. The transaction remains encrypted in the mempool until + validators decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -129,16 +134,28 @@ def add_stake_extrinsic( ) block_before = subtensor.block - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - use_nonce=True, - nonce_key="coldkeypub", - period=period, - raise_error=raise_error, - ) + if with_mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + use_nonce=True, + nonce_key="coldkeypub", + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = subtensor.sim_swap( origin_netuid=0, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index e62d3a2298..0a9c3ac234 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4533,6 +4533,7 @@ def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + with_mev_protection: bool = False, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4554,6 +4555,9 @@ def add_stake( exceed the price tolerance. If false, the entire stake fails if it would exceed the tolerance. rate_tolerance: The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price increase. Only used when safe_staking is True. + with_mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to + protect against front-running and MEV attacks. The transaction remains encrypted in the mempool until + validators decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4578,6 +4582,7 @@ def add_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + with_mev_protection=with_mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, From 85161e2ae94c5081f88ffc48b915092f9b7259a9 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 23:08:49 -0800 Subject: [PATCH 24/67] fix conftest --- tests/e2e_tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 7783a55f2a..89b66c3368 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -67,7 +67,7 @@ def local_chain(request): # passed env variable to control node mod (non-/fast-blocks) fast_blocks = "False" if (os.getenv("FAST_BLOCKS") == "0") is True else "True" - params = f"{fast_blocks}" if args is None else f"{fast_blocks} {args} " + params = f"{fast_blocks}" if args is None else f"{args}" if shutil.which("docker") and not os.getenv("USE_DOCKER") == "0": yield from docker_runner(params) From 9d34fac82125945b7842a869aa9b209e4cb2ef17 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 23:09:04 -0800 Subject: [PATCH 25/67] non fast blocks e2e tests --- tests/e2e_tests/test_mev_shield.py | 48 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/tests/e2e_tests/test_mev_shield.py b/tests/e2e_tests/test_mev_shield.py index 239fd54e33..cdaabd5b45 100644 --- a/tests/e2e_tests/test_mev_shield.py +++ b/tests/e2e_tests/test_mev_shield.py @@ -1,9 +1,8 @@ """E2E tests for MEV Shield functionality.""" -from bittensor_wallet import Wallet +import pytest from bittensor.core.extrinsics import pallets -from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging from tests.e2e_tests.utils import ( @@ -11,11 +10,17 @@ ACTIVATE_SUBNET, REGISTER_NEURON, REGISTER_SUBNET, + SUDO_SET_TEMPO, + NETUID, + AdminUtils ) +TEMPO_TO_SET = 3 + +@pytest.mark.parametrize("local_chain", [False], indirect=True) def test_mev_shield_happy_path( - subtensor, alice_wallet, bob_wallet, charlie_wallet, dave_wallet + subtensor, alice_wallet, bob_wallet, charlie_wallet, dave_wallet, local_chain ): """Tests MEV Shield functionality with add_stake inner call. @@ -35,21 +40,22 @@ def test_mev_shield_happy_path( - Wait for validators to decrypt and execute the transaction (3 blocks) - Verify that the stake has increased after execution """ - bob_sn = TestSubnet(subtensor) bob_sn.execute_steps( [ REGISTER_SUBNET(bob_wallet), + SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, NETUID, TEMPO_TO_SET), ACTIVATE_SUBNET(bob_wallet), REGISTER_NEURON(charlie_wallet), ] ) - # MeV Res logic works not from before third epoch with fast blocks, so we need to wait for it - next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(bob_sn.netuid) - subtensor.wait_for_block( - next_epoch_start_block + subtensor.subnets.tempo(bob_sn.netuid) * 2 - ) + if subtensor.chain.is_fast_blocks(): + # MeV Res logic works not from before third epoch with fast blocks, so we need to wait for it + next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(bob_sn.netuid) + subtensor.wait_for_block( + next_epoch_start_block + subtensor.subnets.tempo(bob_sn.netuid) * 2 + ) for signer in [None, dave_wallet.coldkey]: stake_before = subtensor.staking.get_stake( @@ -78,6 +84,7 @@ def test_mev_shield_happy_path( signer_keypair=signer, raise_error=True, ) + assert response.success, response.message assert response.data.get("revealed_extrinsic_receipt") is not None, ( "No revealed extrinsic receipt." @@ -96,8 +103,10 @@ def test_mev_shield_happy_path( assert stake_after > stake_before +@pytest.mark.parametrize("local_chain", [False], indirect=True) +@pytest.mark.asyncio async def test_mev_shield_happy_path_async( - async_subtensor, alice_wallet, bob_wallet, charlie_wallet, dave_wallet + async_subtensor, alice_wallet, bob_wallet, charlie_wallet, dave_wallet, local_chain ): """Async tests MEV Shield functionality with add_stake inner call. @@ -122,18 +131,20 @@ async def test_mev_shield_happy_path_async( await bob_sn.async_execute_steps( [ REGISTER_SUBNET(bob_wallet), + SUDO_SET_TEMPO(alice_wallet, AdminUtils, True, NETUID, TEMPO_TO_SET), ACTIVATE_SUBNET(bob_wallet), REGISTER_NEURON(charlie_wallet), ] ) - # MeV Res logic works not from before third epoch with fast blocks, so we need to wait for it - next_epoch_start_block = await async_subtensor.subnets.get_next_epoch_start_block( - bob_sn.netuid - ) - await async_subtensor.wait_for_block( - next_epoch_start_block + await async_subtensor.subnets.tempo(bob_sn.netuid) * 2 - ) + if await async_subtensor.chain.is_fast_blocks(): + # MeV Res logic works not from before third epoch with fast blocks, so we need to wait for it + next_epoch_start_block = await async_subtensor.subnets.get_next_epoch_start_block( + bob_sn.netuid + ) + await async_subtensor.wait_for_block( + next_epoch_start_block + await async_subtensor.subnets.tempo(bob_sn.netuid) * 2 + ) for signer in [None, dave_wallet.coldkey]: stake_before = await async_subtensor.staking.get_stake( @@ -148,7 +159,7 @@ async def test_mev_shield_happy_path_async( subnet_price = await async_subtensor.subnets.get_subnet_price(2) limit_price = (subnet_price * 2).rao - call = pallets.SubtensorModule(async_subtensor).add_stake_limit( + call = await pallets.SubtensorModule(async_subtensor).add_stake_limit( netuid=bob_sn.netuid, hotkey=charlie_wallet.hotkey.ss58_address, amount_staked=Balance.from_tao(5).rao, @@ -162,6 +173,7 @@ async def test_mev_shield_happy_path_async( signer_keypair=signer, raise_error=True, ) + assert response.success, response.message assert response.data.get("revealed_extrinsic_receipt") is not None, ( "No revealed extrinsic receipt." From a4e1adf2e88b1f849e24c2f6e72543a6657b5a38 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 23:09:15 -0800 Subject: [PATCH 26/67] ruff --- tests/e2e_tests/test_mev_shield.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/e2e_tests/test_mev_shield.py b/tests/e2e_tests/test_mev_shield.py index cdaabd5b45..fb7e45a22a 100644 --- a/tests/e2e_tests/test_mev_shield.py +++ b/tests/e2e_tests/test_mev_shield.py @@ -12,7 +12,7 @@ REGISTER_SUBNET, SUDO_SET_TEMPO, NETUID, - AdminUtils + AdminUtils, ) TEMPO_TO_SET = 3 @@ -52,7 +52,9 @@ def test_mev_shield_happy_path( if subtensor.chain.is_fast_blocks(): # MeV Res logic works not from before third epoch with fast blocks, so we need to wait for it - next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(bob_sn.netuid) + next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block( + bob_sn.netuid + ) subtensor.wait_for_block( next_epoch_start_block + subtensor.subnets.tempo(bob_sn.netuid) * 2 ) @@ -139,11 +141,12 @@ async def test_mev_shield_happy_path_async( if await async_subtensor.chain.is_fast_blocks(): # MeV Res logic works not from before third epoch with fast blocks, so we need to wait for it - next_epoch_start_block = await async_subtensor.subnets.get_next_epoch_start_block( - bob_sn.netuid + next_epoch_start_block = ( + await async_subtensor.subnets.get_next_epoch_start_block(bob_sn.netuid) ) await async_subtensor.wait_for_block( - next_epoch_start_block + await async_subtensor.subnets.tempo(bob_sn.netuid) * 2 + next_epoch_start_block + + await async_subtensor.subnets.tempo(bob_sn.netuid) * 2 ) for signer in [None, dave_wallet.coldkey]: From 192dfc38c7b94f6f39008596190f5549cbb4a376 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 23:20:01 -0800 Subject: [PATCH 27/67] unit test fix --- tests/unit_tests/test_subtensor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 4e6ca74220..7ad64f7f33 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2682,7 +2682,7 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): wallet=fake_wallet, hotkey_ss58=fake_hotkey_ss58, netuid=14, - amount=fake_amount.rao, + amount=fake_amount, wait_for_inclusion=True, wait_for_finalization=False, safe_staking=True, @@ -2690,6 +2690,7 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): rate_tolerance=fake_rate_tolerance, period=DEFAULT_PERIOD, raise_error=False, + with_mev_protection=False, ) assert result == mock_add_stake_extrinsic.return_value From f9549501811fac9603f158bdb0ea305e82ae6533 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 1 Dec 2025 23:21:43 -0800 Subject: [PATCH 28/67] unit test fix (one more) --- tests/unit_tests/test_subtensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 7ad64f7f33..891a30cc40 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2647,6 +2647,7 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): rate_tolerance=0.005, period=DEFAULT_PERIOD, raise_error=False, + with_mev_protection=False, ) assert result == mock_add_stake_extrinsic.return_value From f0c0eaaf033f8d0e5e8262fa9001343c4d8f26e8 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 15:16:59 -0800 Subject: [PATCH 29/67] update `ExtrinsicResponse` --- bittensor/core/types.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/bittensor/core/types.py b/bittensor/core/types.py index af1e7dd2bc..0ab4a02b20 100644 --- a/bittensor/core/types.py +++ b/bittensor/core/types.py @@ -301,6 +301,11 @@ class ExtrinsicResponse: contains the most detailed execution data available, including the block number and hash, triggered events, extrinsic index, execution phase, and other low-level details. This allows deep debugging or post-analysis of on-chain execution. + mev_extrinsic_receipt: The receipt object of the revealed (decrypted and executed) MEV Shield extrinsic. This is + populated when using MEV Shield protection (``with_mev_protection=True``) and contains the execution details + of the second extrinsic that decrypts and executes the originally encrypted call. Contains triggered events + such as ``DecryptedExecuted`` or ``DecryptedRejected``, block information, and other execution metadata. Set + to ``None`` for non-MEV Shield transactions or when the revealed extrinsic receipt is not available. transaction_tao_fee: TAO fee charged by the transaction in TAO (e.g., fee for add_stake), if available. transaction_alpha_fee: Alpha fee charged by the transaction (e.g., fee for transfer_stake), if available. error: Captures the underlying exception if the extrinsic failed, otherwise `None`. @@ -329,10 +334,11 @@ class ExtrinsicResponse: message: Successfully registered subnet extrinsic_function: register_subnet_extrinsic extrinsic: {'account_id': '0xd43593c715fdd31c... + transaction_fee: τ1.0 + extrinsic_receipt: Extrinsic Receipt data of of the submitted extrinsic + mev_extrinsic_receipt: None transaction_tao_fee: τ1.0 transaction_alpha_fee: 1.0β - extrinsic_receipt: Extrinsic Receipt data of of the submitted extrinsic - transaction_fee: τ1.0 error: None data: None @@ -353,6 +359,7 @@ class ExtrinsicResponse: extrinsic: Optional["GenericExtrinsic"] = None extrinsic_fee: Optional["Balance"] = None extrinsic_receipt: Optional["AsyncExtrinsicReceipt | ExtrinsicReceipt"] = None + mev_extrinsic_receipt: Optional["AsyncExtrinsicReceipt | ExtrinsicReceipt"] = None transaction_tao_fee: Optional["Balance"] = None transaction_alpha_fee: Optional["Balance"] = None error: Optional[Exception] = None @@ -378,8 +385,8 @@ def __str__(self): f"\textrinsic_receipt: {_extrinsic_receipt}" f"\ttransaction_tao_fee: {self.transaction_tao_fee}\n" f"\ttransaction_alpha_fee: {self.transaction_alpha_fee}\n" - f"\tdata: {self.data}\n" f"\terror: {self.error}" + f"\tdata: {self.data}\n" ) def __repr__(self): From 849317c90e15e99ac378bce1dc4b7aaaec771366 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 15:17:24 -0800 Subject: [PATCH 30/67] improve `get_event_attributes_by_event_name` --- bittensor/core/extrinsics/utils.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 5ddd96ac72..ab543dce35 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -1,6 +1,7 @@ """Module with helper functions for extrinsics.""" import hashlib +import logging from typing import TYPE_CHECKING, Optional, Union from bittensor_drand import encrypt_mlkem768, mlkem_kdf_id @@ -288,7 +289,7 @@ def get_mev_commitment_and_ciphertext( return commitment_hex, ciphertext, payload_core, signature -def get_event_data(triggered_events: list, event_id: str) -> Optional[dict]: +def get_event_attributes_by_event_name(events: list, event_name: str) -> Optional[dict]: """ Extracts event data from triggered events by event ID. @@ -296,13 +297,23 @@ def get_event_data(triggered_events: list, event_id: str) -> Optional[dict]: specified event_id. Parameters: - triggered_events: List of event dictionaries, typically from ExtrinsicReceipt.triggered_events. Each event - should have an "event_id" key and an "attributes" key. - event_id: The event identifier to search for (e.g., "EncryptedSubmitted", "DecryptedExecuted"). + events: List of event dictionaries, typically from ExtrinsicReceipt.triggered_events. Each event should have an + "module_id". "event_id" key and an "attributes" key. + event_name: The events identifier to search for (e.g. "mevShield.EncryptedSubmitted", etc). Returns: The attributes dictionary of the matching event, or None if no matching event is found.""" - for event in triggered_events: - if event["event_id"] == event_id: - return event["attributes"] + for event in events: + try: + module_id, event_id = event_name.split(".") + except (ValueError, AttributeError): + logging.debug( + "Invalid event_name. Should be string as `module_id.event_id` e.g. `mevShield.EncryptedSubmitted`." + ) + return None + if ( + event["module_id"].lower() == module_id.lower() + and event["event_id"].lower() == event_id.lower() + ): + return event return None From 14ff6fc98c550ff8f32e731e4752e9252fe921fa Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 15:25:06 -0800 Subject: [PATCH 31/67] update extrinsics --- .../core/extrinsics/asyncex/mev_shield.py | 75 +++++++++---------- bittensor/core/extrinsics/mev_shield.py | 75 +++++++++---------- 2 files changed, 74 insertions(+), 76 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index b6710ab506..2b54a1ec35 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -6,7 +6,7 @@ from bittensor.core.extrinsics.pallets import MevShield from bittensor.core.extrinsics.utils import ( - get_event_data, + get_event_attributes_by_event_name, get_mev_commitment_and_ciphertext, ) from bittensor.core.types import ExtrinsicResponse @@ -21,11 +21,11 @@ async def find_revealed_extrinsic( subtensor: "AsyncSubtensor", signer_ss58: str, - event_id: str, + event_names: list[str], event_hash_id: str, start_block_hash: str, blocks_ahead: int = 5, -) -> Optional["AsyncExtrinsicReceipt"]: +) -> Optional[tuple[str, "AsyncExtrinsicReceipt"]]: """ Searches for an extrinsic containing a specific MEV Shield event in subsequent blocks. @@ -37,7 +37,7 @@ async def find_revealed_extrinsic( subtensor: The Subtensor instance used for blockchain queries. signer_ss58: The SS58 address of the signer account. Used to verify that the event belongs to the correct transaction (matches the "signer" attribute in the event). - event_id: The event identifier to search for. Typically "DecryptedExecuted" or "DecryptedRejected" for MEV + event_names: The event identifiers to search for. Typically "DecryptedExecuted" or "DecryptedRejected" for MEV Shield transactions. event_hash_id: The wrapper_id (hash of (author, commitment, ciphertext)) to match. This uniquely identifies a specific MEV Shield submission. @@ -48,8 +48,7 @@ async def find_revealed_extrinsic( as execute_revealed will be in subsequent blocks). Returns: - The ExtrinsicReceipt object for the extrinsic containing the matching event, or None if the event is not found - within the specified block range. + Tuple with event name and ExtrinsicReceipt object. """ start_block_number = await subtensor.substrate.get_block_number(start_block_hash) @@ -60,29 +59,24 @@ async def find_revealed_extrinsic( current_block_hash = await subtensor.substrate.get_block_hash( current_block_number ) - extrinsics = await subtensor.substrate.get_extrinsics(current_block_hash) + events = await subtensor.substrate.get_events(current_block_hash) except Exception as e: logging.debug( f"Error getting extrinsics for block `{current_block_number}`: {e}" ) continue - for idx, e in enumerate(extrinsics): - extrinsic_ = AsyncExtrinsicReceipt( - substrate=subtensor.substrate, - extrinsic_hash=e.extrinsic_hash, - block_hash=current_block_hash, - extrinsic_idx=idx, - ) - - if triggered_events := await extrinsic_.triggered_events: - event_data = get_event_data(triggered_events, event_id) + for event_name in event_names: + if event := get_event_attributes_by_event_name(events, event_name): if ( - event_data - and event_hash_id == event_data["id"] - and signer_ss58 == event_data["signer"] + event["attributes"]["signer"] == signer_ss58 + and event["attributes"]["id"] == event_hash_id ): - return extrinsic_ + return event_name, AsyncExtrinsicReceipt( + substrate=subtensor.substrate, + block_hash=current_block_hash, + extrinsic_idx=event["extrinsic_idx"], + ) await subtensor.wait_for_block() @@ -153,14 +147,10 @@ async def submit_encrypted_extrinsic( ml_kem_768_public_key = await subtensor.get_mev_shield_next_key() if ml_kem_768_public_key is None: - # Fallback to CurrentKey if NextKey is not available - current_key_result = await subtensor.get_mev_shield_current_key() - if current_key_result is None: - return ExtrinsicResponse.from_exception( - raise_error=raise_error, - error=ValueError("MEV Shield NextKey not available in storage."), - ) - ml_kem_768_public_key = current_key_result + return ExtrinsicResponse.from_exception( + raise_error=raise_error, + error=ValueError("MEV Shield NextKey not available in storage."), + ) genesis_hash = await subtensor.get_block_hash(block=0) @@ -200,22 +190,31 @@ async def submit_encrypted_extrinsic( wait_for_revealed_execution, int ): triggered_events = await response.extrinsic_receipt.triggered_events - event_hash_id = get_event_data(triggered_events, "EncryptedSubmitted")[ - "id" - ] + event_hash_id = get_event_attributes_by_event_name( + events=triggered_events, event_name="mevShield.EncryptedSubmitted" + )["attributes"]["id"] - revealed_extrinsic_receipt = await find_revealed_extrinsic( + revealed_extrinsic = await find_revealed_extrinsic( subtensor=subtensor, signer_ss58=signer_keypair.ss58_address, - event_id="DecryptedExecuted", + event_names=[ + "mevShield.DecryptedExecuted", + "mevShield.DecryptedRejected", + ], event_hash_id=event_hash_id, start_block_hash=response.extrinsic_receipt.block_hash, blocks_ahead=wait_for_revealed_execution, ) - if revealed_extrinsic_receipt: - response.data.update( - {"revealed_extrinsic_receipt": revealed_extrinsic_receipt} - ) + if revealed_extrinsic and isinstance(revealed_extrinsic, tuple): + event_name, response.mev_extrinsic_receipt = revealed_extrinsic + + if event_name == "mevShield.DecryptedRejected": + response.success = False + response.message = "DecryptedRejected event found." + return response.from_exception( + raise_error=raise_error, + error=RuntimeError(response.message), + ) else: response.success = False response.error = RuntimeError(f"DecryptedExecuted event not found.") diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index d4d78f8265..3ca46d0dc4 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -6,7 +6,7 @@ from bittensor.core.extrinsics.pallets import MevShield from bittensor.core.extrinsics.utils import ( - get_event_data, + get_event_attributes_by_event_name, get_mev_commitment_and_ciphertext, ) from bittensor.core.types import ExtrinsicResponse @@ -21,11 +21,11 @@ def find_revealed_extrinsic( subtensor: "Subtensor", signer_ss58: str, - event_id: str, + event_names: list[str], event_hash_id: str, start_block_hash: str, blocks_ahead: int = 5, -) -> Optional["ExtrinsicReceipt"]: +) -> Optional[tuple[str, "ExtrinsicReceipt"]]: """ Searches for an extrinsic containing a specific MEV Shield event in subsequent blocks. @@ -37,7 +37,7 @@ def find_revealed_extrinsic( subtensor: The Subtensor instance used for blockchain queries. signer_ss58: The SS58 address of the signer account. Used to verify that the event belongs to the correct transaction (matches the "signer" attribute in the event). - event_id: The event identifier to search for. Typically "DecryptedExecuted" or "DecryptedRejected" for MEV + event_names: The event identifiers to search for. Typically "DecryptedExecuted" or "DecryptedRejected" for MEV Shield transactions. event_hash_id: The wrapper_id (hash of (author, commitment, ciphertext)) to match. This uniquely identifies a specific MEV Shield submission. @@ -48,8 +48,7 @@ def find_revealed_extrinsic( as execute_revealed will be in subsequent blocks). Returns: - The ExtrinsicReceipt object for the extrinsic containing the matching event, or None if the event is not found - within the specified block range. + Tuple with event name and ExtrinsicReceipt object. """ start_block_number = subtensor.substrate.get_block_number(start_block_hash) @@ -60,29 +59,24 @@ def find_revealed_extrinsic( current_block_hash = subtensor.substrate.get_block_hash( current_block_number ) - extrinsics = subtensor.substrate.get_extrinsics(current_block_hash) + events = subtensor.substrate.get_events(current_block_hash) except Exception as e: logging.debug( f"Error getting extrinsics for block `{current_block_number}`: {e}" ) continue - for idx, e in enumerate(extrinsics): - extrinsic_ = ExtrinsicReceipt( - substrate=subtensor.substrate, - extrinsic_hash=e.extrinsic_hash, - block_hash=current_block_hash, - extrinsic_idx=idx, - ) - - if triggered_events := extrinsic_.triggered_events: - event_data = get_event_data(triggered_events, event_id) + for event_name in event_names: + if event := get_event_attributes_by_event_name(events, event_name): if ( - event_data - and event_hash_id == event_data["id"] - and signer_ss58 == event_data["signer"] + event["attributes"]["signer"] == signer_ss58 + and event["attributes"]["id"] == event_hash_id ): - return extrinsic_ + return event_name, ExtrinsicReceipt( + substrate=subtensor.substrate, + block_hash=current_block_hash, + extrinsic_idx=event["extrinsic_idx"], + ) subtensor.wait_for_block() @@ -153,14 +147,10 @@ def submit_encrypted_extrinsic( ml_kem_768_public_key = subtensor.get_mev_shield_next_key() if ml_kem_768_public_key is None: - # Fallback to CurrentKey if NextKey is not available - current_key_result = subtensor.get_mev_shield_current_key() - if current_key_result is None: - return ExtrinsicResponse.from_exception( - raise_error=raise_error, - error=ValueError("MEV Shield NextKey not available in storage."), - ) - ml_kem_768_public_key = current_key_result + return ExtrinsicResponse.from_exception( + raise_error=raise_error, + error=ValueError("MEV Shield NextKey not available in storage."), + ) genesis_hash = subtensor.get_block_hash(block=0) @@ -200,22 +190,31 @@ def submit_encrypted_extrinsic( wait_for_revealed_execution, int ): triggered_events = response.extrinsic_receipt.triggered_events - event_hash_id = get_event_data(triggered_events, "EncryptedSubmitted")[ - "id" - ] + event_hash_id = get_event_attributes_by_event_name( + events=triggered_events, event_name="mevShield.EncryptedSubmitted" + )["attributes"]["id"] - revealed_extrinsic_receipt = find_revealed_extrinsic( + revealed_extrinsic = find_revealed_extrinsic( subtensor=subtensor, signer_ss58=signer_keypair.ss58_address, - event_id="DecryptedExecuted", + event_names=[ + "mevShield.DecryptedExecuted", + "mevShield.DecryptedRejected", + ], event_hash_id=event_hash_id, start_block_hash=response.extrinsic_receipt.block_hash, blocks_ahead=wait_for_revealed_execution, ) - if revealed_extrinsic_receipt: - response.data.update( - {"revealed_extrinsic_receipt": revealed_extrinsic_receipt} - ) + if revealed_extrinsic and isinstance(revealed_extrinsic, tuple): + event_name, response.mev_extrinsic_receipt = revealed_extrinsic + + if event_name == "mevShield.DecryptedRejected": + response.success = False + response.message = "DecryptedRejected event found." + return response.from_exception( + raise_error=raise_error, + error=RuntimeError(response.message), + ) else: response.success = False response.error = RuntimeError(f"DecryptedExecuted event not found.") From 8310566b1e83c4bd848b524eaa110f2bff87d54c Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 15:25:53 -0800 Subject: [PATCH 32/67] improve e2e tests --- tests/e2e_tests/test_mev_shield.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/e2e_tests/test_mev_shield.py b/tests/e2e_tests/test_mev_shield.py index fb7e45a22a..d54934599d 100644 --- a/tests/e2e_tests/test_mev_shield.py +++ b/tests/e2e_tests/test_mev_shield.py @@ -88,12 +88,10 @@ def test_mev_shield_happy_path( ) assert response.success, response.message - assert response.data.get("revealed_extrinsic_receipt") is not None, ( + assert response.mev_extrinsic_receipt is not None, ( "No revealed extrinsic receipt." ) - # subtensor.wait_for_block(subtensor.block + 3) - stake_after = subtensor.staking.get_stake( coldkey_ss58=signer.ss58_address if signer is not None @@ -178,12 +176,10 @@ async def test_mev_shield_happy_path_async( ) assert response.success, response.message - assert response.data.get("revealed_extrinsic_receipt") is not None, ( + assert response.mev_extrinsic_receipt is not None, ( "No revealed extrinsic receipt." ) - # await async_subtensor.wait_for_block(await async_subtensor.block + 3) - stake_after = await async_subtensor.staking.get_stake( coldkey_ss58=signer.ss58_address if signer is not None From 752f500d10fbc10f9cf1511c569319f3db04e9c7 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 15:41:40 -0800 Subject: [PATCH 33/67] rename `with_mev_protection` to `mev_protection` --- bittensor/core/extrinsics/asyncex/staking.py | 10 +++++----- bittensor/core/extrinsics/staking.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index 67209e64d6..e1bba100d0 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -26,7 +26,7 @@ async def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - with_mev_protection: bool = False, + mev_protection: bool = False, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -46,9 +46,9 @@ async def add_stake_extrinsic( safe_staking: If True, enables price safety checks. allow_partial_stake: If True, allows partial unstaking if price tolerance exceeded. rate_tolerance: Maximum allowed price increase percentage (0.005 = 0.5%). - with_mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to - protect against front-running and MEV attacks. The transaction remains encrypted in the mempool until - validators decrypt and execute it. If False, submits the transaction directly without encryption. + mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -139,7 +139,7 @@ async def add_stake_extrinsic( ) block_hash_before = await subtensor.get_block_hash() - if with_mev_protection: + if mev_protection: response = await submit_encrypted_extrinsic( subtensor=subtensor, wallet=wallet, diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 7118f57caf..d2a2ae6228 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -25,7 +25,7 @@ def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - with_mev_protection: bool = False, + mev_protection: bool = False, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -45,9 +45,9 @@ def add_stake_extrinsic( safe_staking: If True, enables price safety checks. allow_partial_stake: If True, allows partial unstaking if price tolerance exceeded. rate_tolerance: Maximum allowed price increase percentage (0.005 = 0.5%). - with_mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to - protect against front-running and MEV attacks. The transaction remains encrypted in the mempool until - validators decrypt and execute it. If False, submits the transaction directly without encryption. + mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -134,7 +134,7 @@ def add_stake_extrinsic( ) block_before = subtensor.block - if with_mev_protection: + if mev_protection: response = submit_encrypted_extrinsic( subtensor=subtensor, wallet=wallet, From 9472dcbf6ee95908d59906b97dc6c0cf6daa23fb Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 15:47:07 -0800 Subject: [PATCH 34/67] rename `with_mev_protection` to `mev_protection` + docstrings --- bittensor/core/async_subtensor.py | 7 ++++--- bittensor/core/extrinsics/asyncex/staking.py | 1 + bittensor/core/extrinsics/staking.py | 1 + bittensor/core/subtensor.py | 7 ++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 7a08c878f8..3715a457ac 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5810,7 +5810,8 @@ async def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - with_mev_protection: bool = False, + mev_protection: bool = False, + *, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5832,7 +5833,7 @@ async def add_stake( exceed the price tolerance. If false, the entire stake fails if it would exceed the tolerance. rate_tolerance: The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price increase. Only used when safe_staking is True. - with_mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to + mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to protect against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If @@ -5859,7 +5860,7 @@ async def add_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, - with_mev_protection=with_mev_protection, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index e1bba100d0..87eb0b1bb4 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -27,6 +27,7 @@ async def add_stake_extrinsic( allow_partial_stake: bool = False, rate_tolerance: float = 0.005, mev_protection: bool = False, + *, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index d2a2ae6228..3904b4a4ee 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -26,6 +26,7 @@ def add_stake_extrinsic( allow_partial_stake: bool = False, rate_tolerance: float = 0.005, mev_protection: bool = False, + *, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 0a9c3ac234..5c0e3ba258 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4533,7 +4533,8 @@ def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - with_mev_protection: bool = False, + mev_protection: bool = False, + *, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4555,7 +4556,7 @@ def add_stake( exceed the price tolerance. If false, the entire stake fails if it would exceed the tolerance. rate_tolerance: The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price increase. Only used when safe_staking is True. - with_mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to + mev_protection: If True, encrypts and submits the staking transaction through the MEV Shield pallet to protect against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If @@ -4582,7 +4583,7 @@ def add_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, - with_mev_protection=with_mev_protection, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, From f46cfd9cfe80c68c4954b6f557ebc0aa1f8c6b60 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 15:47:14 -0800 Subject: [PATCH 35/67] fix unit tests --- tests/unit_tests/test_subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 891a30cc40..71da16d352 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2647,7 +2647,7 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): rate_tolerance=0.005, period=DEFAULT_PERIOD, raise_error=False, - with_mev_protection=False, + mev_protection=False, ) assert result == mock_add_stake_extrinsic.return_value @@ -2691,7 +2691,7 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): rate_tolerance=fake_rate_tolerance, period=DEFAULT_PERIOD, raise_error=False, - with_mev_protection=False, + mev_protection=False, ) assert result == mock_add_stake_extrinsic.return_value From c654916e0257ed8ec78cadedeec2cc2e377caef8 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 15:50:39 -0800 Subject: [PATCH 36/67] weird code --- bittensor/core/extrinsics/asyncex/staking.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index 87eb0b1bb4..7664da5e13 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -90,8 +90,6 @@ async def add_stake_extrinsic( if amount > old_balance - existential_deposit: # If we are staking all, we need to leave at least the existential deposit. amount = old_balance - existential_deposit - else: - amount = amount # Check enough to stake. if amount > old_balance: From 36478664d34b5a7668af26c4cc5a97769ef88ad5 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 18:03:48 -0800 Subject: [PATCH 37/67] update `submit_encrypted_extrinsic` --- .../core/extrinsics/asyncex/mev_shield.py | 20 ++++++++-------- bittensor/core/extrinsics/mev_shield.py | 23 ++++++++++--------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index 2b54a1ec35..2a9b863374 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -92,7 +92,8 @@ async def submit_encrypted_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - wait_for_revealed_execution: Optional[int] = 5, + wait_for_revealed_execution: bool = True, + blocks_for_revealed_execution: int = 5, ) -> ExtrinsicResponse: """ Submits an encrypted extrinsic to the MEV Shield pallet. @@ -111,11 +112,12 @@ async def submit_encrypted_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. - wait_for_revealed_execution: Maximum number of blocks to wait for the DecryptedExecuted event, indicating that - node validators have successfully decrypted and executed the inner call via execute_revealed. If None, the - function will not wait for revealed execution. If an integer (default: 5), the function will poll up to that - many blocks after inclusion, checking for the DecryptedExecuted event matching this submission's commitment. - The function returns immediately if the event is found before the block limit is reached. + wait_for_revealed_execution: Whether to wait for the DecryptedExecuted event, indicating that validators have + successfully decrypted and executed the inner call. If True, the function will poll subsequent blocks for + the event matching this submission's commitment. + blocks_for_revealed_execution: Maximum number of blocks to poll for the DecryptedExecuted event after inclusion. + The function checks blocks from start_block + 1 to start_block + blocks_for_revealed_execution. Returns + immediately if the event is found before the block limit is reached. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -186,9 +188,7 @@ async def submit_encrypted_extrinsic( "signature": signature, "submitting_id": extrinsic_call.call_hash, } - if wait_for_revealed_execution is not None and isinstance( - wait_for_revealed_execution, int - ): + if wait_for_revealed_execution: triggered_events = await response.extrinsic_receipt.triggered_events event_hash_id = get_event_attributes_by_event_name( events=triggered_events, event_name="mevShield.EncryptedSubmitted" @@ -203,7 +203,7 @@ async def submit_encrypted_extrinsic( ], event_hash_id=event_hash_id, start_block_hash=response.extrinsic_receipt.block_hash, - blocks_ahead=wait_for_revealed_execution, + blocks_ahead=blocks_for_revealed_execution, ) if revealed_extrinsic and isinstance(revealed_extrinsic, tuple): event_name, response.mev_extrinsic_receipt = revealed_extrinsic diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index 3ca46d0dc4..9d6e99a7aa 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -92,7 +92,8 @@ def submit_encrypted_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - wait_for_revealed_execution: Optional[int] = 5, + wait_for_revealed_execution: bool = True, + blocks_for_revealed_execution: int = 5, ) -> ExtrinsicResponse: """ Submits an encrypted extrinsic to the MEV Shield pallet. @@ -111,11 +112,12 @@ def submit_encrypted_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. - wait_for_revealed_execution: Maximum number of blocks to wait for the DecryptedExecuted event, indicating that - node validators have successfully decrypted and executed the inner call via execute_revealed. If None, the - function will not wait for revealed execution. If an integer (default: 5), the function will poll up to that - many blocks after inclusion, checking for the DecryptedExecuted event matching this submission's commitment. - The function returns immediately if the event is found before the block limit is reached. + wait_for_revealed_execution: Whether to wait for the DecryptedExecuted event, indicating that validators have + successfully decrypted and executed the inner call. If True, the function will poll subsequent blocks for + the event matching this submission's commitment. + blocks_for_revealed_execution: Maximum number of blocks to poll for the DecryptedExecuted event after inclusion. + The function checks blocks from start_block + 1 to start_block + blocks_for_revealed_execution. Returns + immediately if the event is found before the block limit is reached. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -186,12 +188,11 @@ def submit_encrypted_extrinsic( "signature": signature, "submitting_id": extrinsic_call.call_hash, } - if wait_for_revealed_execution is not None and isinstance( - wait_for_revealed_execution, int - ): + if wait_for_revealed_execution: triggered_events = response.extrinsic_receipt.triggered_events event_hash_id = get_event_attributes_by_event_name( - events=triggered_events, event_name="mevShield.EncryptedSubmitted" + events=triggered_events, + event_name="mevShield.EncryptedSubmitted", # type: ignore )["attributes"]["id"] revealed_extrinsic = find_revealed_extrinsic( @@ -203,7 +204,7 @@ def submit_encrypted_extrinsic( ], event_hash_id=event_hash_id, start_block_hash=response.extrinsic_receipt.block_hash, - blocks_ahead=wait_for_revealed_execution, + blocks_ahead=blocks_for_revealed_execution, ) if revealed_extrinsic and isinstance(revealed_extrinsic, tuple): event_name, response.mev_extrinsic_receipt = revealed_extrinsic From 12244c56235fffc98e4acd5a075dc3dc344d58d5 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 18:04:39 -0800 Subject: [PATCH 38/67] add `mev_protection` to all sync extrinsics --- bittensor/core/extrinsics/asyncex/staking.py | 3 +- bittensor/core/extrinsics/children.py | 68 +++- bittensor/core/extrinsics/crowdloan.py | 299 ++++++++++++---- bittensor/core/extrinsics/liquidity.py | 134 ++++++-- bittensor/core/extrinsics/move_stake.py | 101 ++++-- bittensor/core/extrinsics/proxy.py | 338 ++++++++++++++----- bittensor/core/extrinsics/registration.py | 134 ++++++-- bittensor/core/extrinsics/root.py | 101 ++++-- bittensor/core/extrinsics/serving.py | 79 +++-- bittensor/core/extrinsics/staking.py | 42 ++- bittensor/core/extrinsics/start_call.py | 35 +- bittensor/core/extrinsics/take.py | 35 +- bittensor/core/extrinsics/transfer.py | 40 ++- bittensor/core/extrinsics/unstaking.py | 83 +++-- bittensor/core/extrinsics/weights.py | 159 ++++++--- bittensor/core/settings.py | 4 + 16 files changed, 1256 insertions(+), 399 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index 7664da5e13..f6f866a09d 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -7,6 +7,7 @@ from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule from bittensor.core.extrinsics.utils import get_old_stakes +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse, UIDs from bittensor.utils import format_error_message from bittensor.utils.balance import Balance @@ -26,8 +27,8 @@ async def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - mev_protection: bool = False, *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, diff --git a/bittensor/core/extrinsics/children.py b/bittensor/core/extrinsics/children.py index 6f5ae76244..5aac2f4ef3 100644 --- a/bittensor/core/extrinsics/children.py +++ b/bittensor/core/extrinsics/children.py @@ -1,6 +1,8 @@ from typing import TYPE_CHECKING, Optional +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule, Sudo +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils import float_to_u64 @@ -15,6 +17,8 @@ def set_children_extrinsic( hotkey_ss58: str, netuid: int, children: list[tuple[float, str]], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -29,6 +33,9 @@ def set_children_extrinsic( hotkey_ss58: The ``SS58`` address of the neuron's hotkey. netuid: The netuid value. children: A list of children with their proportions. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -67,14 +74,26 @@ def set_children_extrinsic( ], ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) return response except Exception as error: @@ -85,6 +104,8 @@ def root_set_pending_childkey_cooldown_extrinsic( subtensor: "Subtensor", wallet: "Wallet", cooldown: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -97,6 +118,9 @@ def root_set_pending_childkey_cooldown_extrinsic( subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). cooldown: The cooldown period in blocks. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -119,14 +143,26 @@ def root_set_pending_childkey_cooldown_extrinsic( sudo_call = Sudo(subtensor).sudo(call=call) - response = subtensor.sign_and_send_extrinsic( - call=sudo_call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=sudo_call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=sudo_call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) return response except Exception as error: diff --git a/bittensor/core/extrinsics/crowdloan.py b/bittensor/core/extrinsics/crowdloan.py index 33c0d35574..81d721ab79 100644 --- a/bittensor/core/extrinsics/crowdloan.py +++ b/bittensor/core/extrinsics/crowdloan.py @@ -1,6 +1,8 @@ from typing import TYPE_CHECKING, Optional +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Crowdloan +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import check_balance_amount @@ -16,6 +18,8 @@ def contribute_crowdloan_extrinsic( wallet: "Wallet", crowdloan_id: int, amount: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -29,6 +33,9 @@ def contribute_crowdloan_extrinsic( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to contribute to. amount: Amount to contribute. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -51,14 +58,26 @@ def contribute_crowdloan_extrinsic( crowdloan_id=crowdloan_id, amount=amount.rao ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -73,6 +92,8 @@ def create_crowdloan_extrinsic( end: int, call: Optional["GenericCall"] = None, target_address: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -90,6 +111,9 @@ def create_crowdloan_extrinsic( end: Block number when the campaign ends. call: Runtime call data (e.g., subtensor::register_leased_network). target_address: SS58 address to transfer funds to on success. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -119,14 +143,26 @@ def create_crowdloan_extrinsic( target_address=target_address, ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -136,6 +172,8 @@ def dissolve_crowdloan_extrinsic( subtensor: "Subtensor", wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -151,6 +189,9 @@ def dissolve_crowdloan_extrinsic( subtensor: Active Subtensor connection. wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to dissolve. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -175,14 +216,26 @@ def dissolve_crowdloan_extrinsic( call = Crowdloan(subtensor).dissolve(crowdloan_id=crowdloan_id) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -192,6 +245,8 @@ def finalize_crowdloan_extrinsic( subtensor: "Subtensor", wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -206,6 +261,9 @@ def finalize_crowdloan_extrinsic( subtensor: Active Subtensor connection. wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to finalize. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. @@ -222,14 +280,26 @@ def finalize_crowdloan_extrinsic( call = Crowdloan(subtensor).finalize(crowdloan_id=crowdloan_id) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -239,6 +309,8 @@ def refund_crowdloan_extrinsic( subtensor: "Subtensor", wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -254,6 +326,9 @@ def refund_crowdloan_extrinsic( subtensor: Active Subtensor connection. wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to refund. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -278,14 +353,26 @@ def refund_crowdloan_extrinsic( call = Crowdloan(subtensor).refund(crowdloan_id=crowdloan_id) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -296,6 +383,8 @@ def update_cap_crowdloan_extrinsic( wallet: "Wallet", crowdloan_id: int, new_cap: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -312,6 +401,9 @@ def update_cap_crowdloan_extrinsic( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_cap: The new fundraising cap (in TAO or Balance). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -339,14 +431,26 @@ def update_cap_crowdloan_extrinsic( crowdloan_id=crowdloan_id, new_cap=new_cap.rao ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -357,6 +461,8 @@ def update_end_crowdloan_extrinsic( wallet: "Wallet", crowdloan_id: int, new_end: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -373,6 +479,9 @@ def update_end_crowdloan_extrinsic( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_end: The new block number at which the crowdloan will end. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -399,14 +508,26 @@ def update_end_crowdloan_extrinsic( crowdloan_id=crowdloan_id, new_end=new_end ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -417,6 +538,8 @@ def update_min_contribution_crowdloan_extrinsic( wallet: "Wallet", crowdloan_id: int, new_min_contribution: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -433,6 +556,9 @@ def update_min_contribution_crowdloan_extrinsic( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_min_contribution: The new minimum contribution amount (in TAO or Balance). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -460,14 +586,26 @@ def update_min_contribution_crowdloan_extrinsic( crowdloan_id=crowdloan_id, new_min_contribution=new_min_contribution.rao ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -477,6 +615,8 @@ def withdraw_crowdloan_extrinsic( subtensor: "Subtensor", wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -489,6 +629,9 @@ def withdraw_crowdloan_extrinsic( subtensor: Active Subtensor connection. wallet: Wallet instance used to sign the transaction (must be unlocked). crowdloan_id: The unique identifier of the crowdloan to withdraw from. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -511,14 +654,26 @@ def withdraw_crowdloan_extrinsic( call = Crowdloan(subtensor).withdraw(crowdloan_id=crowdloan_id) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/liquidity.py b/bittensor/core/extrinsics/liquidity.py index 043bc6d5a2..fbc73588b8 100644 --- a/bittensor/core/extrinsics/liquidity.py +++ b/bittensor/core/extrinsics/liquidity.py @@ -1,6 +1,8 @@ from typing import Optional, TYPE_CHECKING +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Swap +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance from bittensor.utils.liquidity import price_to_tick @@ -18,6 +20,8 @@ def add_liquidity_extrinsic( price_low: Balance, price_high: Balance, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -34,6 +38,9 @@ def add_liquidity_extrinsic( price_low: The lower bound of the price tick range. price_high: The upper bound of the price tick range. hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -64,14 +71,26 @@ def add_liquidity_extrinsic( hotkey=hotkey_ss58 or wallet.hotkey.ss58_address, ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -83,6 +102,8 @@ def modify_liquidity_extrinsic( position_id: int, liquidity_delta: Balance, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -97,6 +118,9 @@ def modify_liquidity_extrinsic( position_id: The id of the position record in the pool. liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative). hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -126,14 +150,26 @@ def modify_liquidity_extrinsic( liquidity_delta=liquidity_delta.rao, ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -144,6 +180,8 @@ def remove_liquidity_extrinsic( netuid: int, position_id: int, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -157,6 +195,9 @@ def remove_liquidity_extrinsic( netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -185,14 +226,26 @@ def remove_liquidity_extrinsic( position_id=position_id, ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -202,6 +255,8 @@ def toggle_user_liquidity_extrinsic( wallet: "Wallet", netuid: int, enable: bool, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -214,6 +269,9 @@ def toggle_user_liquidity_extrinsic( wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. enable: Boolean indicating whether to enable user liquidity. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -235,13 +293,25 @@ def toggle_user_liquidity_extrinsic( enable=enable, ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index 7bc0a86c2a..0f134a9a9b 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -1,6 +1,8 @@ from typing import Optional, TYPE_CHECKING +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -45,6 +47,8 @@ def move_stake_extrinsic( destination_hotkey_ss58: str, amount: Optional[Balance] = None, move_all_stake: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -62,6 +66,9 @@ def move_stake_extrinsic( destination_hotkey_ss58: The SS58 address of the destination hotkey. amount: Amount to move. move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -117,14 +124,26 @@ def move_stake_extrinsic( alpha_amount=amount.rao, ) block_before = subtensor.block - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = subtensor.sim_swap( @@ -181,6 +200,8 @@ def transfer_stake_extrinsic( origin_netuid: int, destination_netuid: int, amount: Balance, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -197,6 +218,9 @@ def transfer_stake_extrinsic( origin_netuid: Network UID of the origin subnet. destination_netuid: Network UID of the destination subnet. amount: The amount of stake to transfer as a `Balance` object. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -247,14 +271,26 @@ def transfer_stake_extrinsic( alpha_amount=amount.rao, ) block_before = subtensor.block - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = subtensor.sim_swap( @@ -305,6 +341,8 @@ def swap_stake_extrinsic( safe_swapping: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -323,6 +361,9 @@ def swap_stake_extrinsic( safe_swapping: If true, enables price safety checks to protect against price impact. allow_partial_stake: If true, allows partial stake swaps when the full amount would exceed the price tolerance. rate_tolerance: Maximum allowed increase in a price ratio (0.005 = 0.5%). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -397,14 +438,26 @@ def swap_stake_extrinsic( ) block_before = subtensor.block - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = subtensor.sim_swap( diff --git a/bittensor/core/extrinsics/proxy.py b/bittensor/core/extrinsics/proxy.py index 1942201af6..8d4307d6b4 100644 --- a/bittensor/core/extrinsics/proxy.py +++ b/bittensor/core/extrinsics/proxy.py @@ -1,8 +1,10 @@ from typing import TYPE_CHECKING, Optional, Union from bittensor.core.chain_data.proxy import ProxyType +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Proxy from bittensor.core.extrinsics.utils import apply_pure_proxy_data +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.btlogging import logging @@ -18,6 +20,8 @@ def add_proxy_extrinsic( delegate_ss58: str, proxy_type: Union[str, ProxyType], delay: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -33,6 +37,9 @@ def add_proxy_extrinsic( proxy_type: The type of proxy permissions (e.g., "Any", "NonTransfer", "Governance", "Staking"). Can be a string or ProxyType enum value. delay: The number of blocks before the proxy can be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -63,14 +70,26 @@ def add_proxy_extrinsic( delay=delay, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Proxy added successfully.[/green]") @@ -89,6 +108,8 @@ def remove_proxy_extrinsic( delegate_ss58: str, proxy_type: Union[str, ProxyType], delay: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -103,6 +124,9 @@ def remove_proxy_extrinsic( delegate_ss58: The SS58 address of the delegate proxy account to remove. proxy_type: The type of proxy permissions to remove. Can be a string or ProxyType enum value. delay: The number of blocks before the proxy removal takes effect. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -133,14 +157,26 @@ def remove_proxy_extrinsic( delay=delay, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Proxy removed successfully.[/green]") @@ -156,6 +192,8 @@ def remove_proxy_extrinsic( def remove_proxies_extrinsic( subtensor: "Subtensor", wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -170,6 +208,9 @@ def remove_proxies_extrinsic( Parameters: subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object (the account whose proxies will be removed). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. @@ -191,14 +232,26 @@ def remove_proxies_extrinsic( call = Proxy(subtensor).remove_proxies() - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]All proxies removed successfully.[/green]") @@ -217,6 +270,8 @@ def create_pure_proxy_extrinsic( proxy_type: Union[str, ProxyType], delay: int, index: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -231,6 +286,9 @@ def create_pure_proxy_extrinsic( proxy_type: The type of proxy permissions for the pure proxy. Can be a string or ProxyType enum value. delay: The number of blocks before the pure proxy can be used. index: The index to use for generating the pure proxy account address. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -268,14 +326,26 @@ def create_pure_proxy_extrinsic( index=index, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Pure proxy created successfully.[/green]") @@ -321,6 +391,8 @@ def kill_pure_proxy_extrinsic( height: int, ext_index: int, force_proxy_type: Optional[Union[str, ProxyType]] = ProxyType.Any, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -353,6 +425,9 @@ def kill_pure_proxy_extrinsic( the pure proxy account should be used. The spawner must have a proxy relationship of this type (or `Any`) with the pure proxy account. Defaults to `ProxyType.Any` for maximum compatibility. If `None`, Substrate will automatically select an available proxy type from the spawner's proxy relationships. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -435,6 +510,7 @@ def kill_pure_proxy_extrinsic( real_account_ss58=pure_proxy_ss58, force_proxy_type=force_proxy_type, call=kill_pure_call, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -458,6 +534,8 @@ def proxy_extrinsic( real_account_ss58: str, force_proxy_type: Optional[Union[str, ProxyType]], call: "GenericCall", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -473,6 +551,9 @@ def proxy_extrinsic( force_proxy_type: The type of proxy to use for the call. If None, any proxy type can be used. Otherwise, must match one of the allowed proxy types. Can be a string or ProxyType enum value. call: The inner call to be executed on behalf of the real account. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -507,14 +588,26 @@ def proxy_extrinsic( call=call, ) - response = subtensor.sign_and_send_extrinsic( - call=proxy_call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=proxy_call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=proxy_call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Proxy call executed successfully.[/green]") @@ -534,6 +627,8 @@ def proxy_announced_extrinsic( real_account_ss58: str, force_proxy_type: Optional[Union[str, ProxyType]], call: "GenericCall", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -553,6 +648,9 @@ def proxy_announced_extrinsic( force_proxy_type: The type of proxy to use for the call. If None, any proxy type can be used. Otherwise, must match one of the allowed proxy types. Can be a string or ProxyType enum value. call: The inner call to be executed on behalf of the real account (must match the announced call_hash). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -588,14 +686,26 @@ def proxy_announced_extrinsic( call=call, ) - response = subtensor.sign_and_send_extrinsic( - call=proxy_call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=proxy_call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=proxy_call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Announced proxy call executed successfully.[/green]") @@ -613,6 +723,8 @@ def announce_extrinsic( wallet: "Wallet", real_account_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -626,6 +738,9 @@ def announce_extrinsic( wallet: Bittensor wallet object (should be the proxy account wallet). real_account_ss58: The SS58 address of the real account on whose behalf the call will be made. call_hash: The hash of the call that will be executed in the future. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -656,14 +771,26 @@ def announce_extrinsic( call_hash=call_hash, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Proxy call announced successfully.[/green]") @@ -681,6 +808,8 @@ def reject_announcement_extrinsic( wallet: "Wallet", delegate_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -697,6 +826,9 @@ def reject_announcement_extrinsic( wallet: Bittensor wallet object (should be the real account wallet). delegate_ss58: The SS58 address of the delegate proxy account whose announcement is being rejected. call_hash: The hash of the call that was announced and is now being rejected. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -727,14 +859,26 @@ def reject_announcement_extrinsic( call_hash=call_hash, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Announcement rejected successfully.[/green]") @@ -752,6 +896,8 @@ def remove_announcement_extrinsic( wallet: "Wallet", real_account_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -768,6 +914,9 @@ def remove_announcement_extrinsic( wallet: Bittensor wallet object (should be the proxy account wallet that made the announcement). real_account_ss58: The SS58 address of the real account on whose behalf the call was announced. call_hash: The hash of the call that was announced and is now being removed. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -798,14 +947,26 @@ def remove_announcement_extrinsic( call_hash=call_hash, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Announcement removed successfully.[/green]") @@ -821,6 +982,8 @@ def remove_announcement_extrinsic( def poke_deposit_extrinsic( subtensor: "Subtensor", wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -836,6 +999,9 @@ def poke_deposit_extrinsic( Parameters: subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object (the account whose deposits will be adjusted). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. @@ -862,14 +1028,26 @@ def poke_deposit_extrinsic( call = Proxy(subtensor).poke_deposit() - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Deposit poked successfully.[/green]") diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index 6474fcdafd..bad3b70eb4 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -6,7 +6,9 @@ from typing import Optional, Union, TYPE_CHECKING from bittensor.core.errors import RegistrationError +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.btlogging import logging from bittensor.utils.registration import create_pow, log_no_torch_error, torch @@ -20,6 +22,8 @@ def burned_register_extrinsic( subtensor: "Subtensor", wallet: "Wallet", netuid: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -31,6 +35,9 @@ def burned_register_extrinsic( subtensor: Subtensor instance. wallet: Bittensor wallet object. netuid: The ``netuid`` of the subnet to register on. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -81,14 +88,26 @@ def burned_register_extrinsic( netuid=netuid, hotkey=wallet.hotkey.ss58_address ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) extrinsic_fee = response.extrinsic_fee logging.debug( f"The registration fee for SN #[blue]{netuid}[/blue] is [blue]{extrinsic_fee}[/blue]." @@ -135,6 +154,8 @@ def burned_register_extrinsic( def register_subnet_extrinsic( subtensor: "Subtensor", wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -146,6 +167,9 @@ def register_subnet_extrinsic( Parameters: subtensor: The subtensor interface to send the extrinsic. wallet: The wallet to be used for subnet registration. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -177,14 +201,26 @@ def register_subnet_extrinsic( hotkey=wallet.hotkey.ss58_address ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) if not wait_for_finalization and not wait_for_inclusion: return response @@ -212,6 +248,8 @@ def register_extrinsic( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -231,6 +269,9 @@ def register_extrinsic( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -336,14 +377,26 @@ def register_extrinsic( nonce=pow_result.nonce, work=[int(byte_) for byte_ in pow_result.seal], ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) if not response.success: # Look error here @@ -397,6 +450,8 @@ def set_subnet_identity_extrinsic( discord: str, description: str, additional: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -417,6 +472,9 @@ def set_subnet_identity_extrinsic( discord: Discord server or contact for the subnet. description: A textual description of the subnet. additional: Any additional metadata or information related to the subnet. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -447,14 +505,26 @@ def set_subnet_identity_extrinsic( additional=additional, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) if not wait_for_finalization and not wait_for_inclusion: return response diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index f26a77b607..bd032847a7 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -2,7 +2,9 @@ from typing import Literal, Optional, TYPE_CHECKING from bittensor.core.chain_data import RootClaimType +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse, UIDs from bittensor.utils import u16_normalized_float from bittensor.utils.balance import Balance @@ -38,6 +40,8 @@ def _get_limits(subtensor: "Subtensor") -> tuple[int, float]: def root_register_extrinsic( subtensor: "Subtensor", wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -49,6 +53,9 @@ def root_register_extrinsic( Parameters: subtensor: Subtensor instance to interact with the blockchain. wallet: Bittensor Wallet instance. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -104,14 +111,26 @@ def root_register_extrinsic( hotkey=wallet.hotkey.ss58_address ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) if not response.success: logging.error(f"[red]{response.message}[/red]") @@ -144,6 +163,8 @@ def set_root_claim_type_extrinsic( subtensor: "Subtensor", wallet: "Wallet", new_root_claim_type: "Literal['Swap', 'Keep'] | RootClaimType | dict", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -159,6 +180,9 @@ def set_root_claim_type_extrinsic( - RootClaimType: RootClaimType.Swap, RootClaimType.Keep - Dict: {"KeepSubnets": {"subnets": [1, 2, 3]}} - Callable: RootClaimType.KeepSubnets([1, 2, 3]) + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -181,14 +205,26 @@ def set_root_claim_type_extrinsic( new_root_claim_type=normalized_type ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -198,6 +234,8 @@ def claim_root_extrinsic( subtensor: "Subtensor", wallet: "Wallet", netuids: "UIDs", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -209,6 +247,9 @@ def claim_root_extrinsic( subtensor: Subtensor instance to interact with the blockchain. wallet: Bittensor Wallet instance. netuids: The netuids to claim root emissions for. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -227,14 +268,26 @@ def claim_root_extrinsic( call = SubtensorModule(subtensor).claim_root(subnets=netuids) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index 673df26780..5dd17c66bc 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -1,8 +1,9 @@ from typing import Optional, Union, TYPE_CHECKING from bittensor.core.errors import MetadataError +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Commitments, SubtensorModule -from bittensor.core.settings import version_as_int +from bittensor.core.settings import DEFAULT_MEV_PROTECTION, version_as_int from bittensor.core.types import AxonServeCallParams, ExtrinsicResponse from bittensor.utils import ( networking as net, @@ -26,6 +27,8 @@ def serve_extrinsic( placeholder1: int = 0, placeholder2: int = 0, certificate: Optional[Certificate] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -44,6 +47,9 @@ def serve_extrinsic( placeholder1: A placeholder for future use. placeholder2: A placeholder for future use. certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -97,15 +103,27 @@ def serve_extrinsic( ) call = call_function(**params.as_dict()) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - sign_with="hotkey", - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", + period=period, + raise_error=raise_error, + ) if response.success: logging.debug( @@ -126,6 +144,8 @@ def serve_axon_extrinsic( netuid: int, axon: "Axon", certificate: Optional["Certificate"] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -139,6 +159,9 @@ def serve_axon_extrinsic( netuid: The ``netuid`` being served on. axon: Axon to serve. certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -177,6 +200,7 @@ def serve_axon_extrinsic( protocol=4, netuid=netuid, certificate=certificate, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -200,6 +224,8 @@ def publish_metadata_extrinsic( data_type: str, data: Union[bytes, dict], reset_bonds: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -218,6 +244,9 @@ def publish_metadata_extrinsic( data: The actual metadata content to be published. This should be formatted or hashed according to the ``type`` specified. (Note: max ``str`` length is 128 bytes for ``'Raw0-128'``.) reset_bonds: If `True`, the function will reset the bonds for the neuron. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -248,15 +277,27 @@ def publish_metadata_extrinsic( call = Commitments(subtensor).set_commitment(netuid=netuid, info=info) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - sign_with=signing_keypair, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + sign_with=signing_keypair, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: return response diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 3904b4a4ee..bd38ace97e 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -6,6 +6,7 @@ from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule from bittensor.core.extrinsics.utils import get_old_stakes +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse, UIDs from bittensor.utils import format_error_message from bittensor.utils.balance import Balance @@ -25,8 +26,8 @@ def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - mev_protection: bool = False, *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -212,6 +213,8 @@ def add_stake_multiple_extrinsic( netuids: UIDs, hotkey_ss58s: list[str], amounts: list[Balance], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -227,6 +230,9 @@ def add_stake_multiple_extrinsic( netuids: List of netuids to stake to. hotkey_ss58s: List of hotkeys to stake to. amounts: List of corresponding TAO amounts to bet for each netuid and hotkey. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -347,6 +353,7 @@ def add_stake_multiple_extrinsic( netuid=netuid, hotkey_ss58=hotkey_ss58, amount=amount, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -429,6 +436,8 @@ def set_auto_stake_extrinsic( wallet: "Wallet", netuid: int, hotkey_ss58: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -442,6 +451,9 @@ def set_auto_stake_extrinsic( netuid: The subnet unique identifier. hotkey_ss58: The SS58 address of the validator's hotkey to which the miner automatically stakes all rewards received from the specified subnet immediately upon receipt. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -462,14 +474,26 @@ def set_auto_stake_extrinsic( netuid=netuid, hotkey=hotkey_ss58 ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) if response.success: logging.debug(response.message) diff --git a/bittensor/core/extrinsics/start_call.py b/bittensor/core/extrinsics/start_call.py index 24c053decf..345ef5eeae 100644 --- a/bittensor/core/extrinsics/start_call.py +++ b/bittensor/core/extrinsics/start_call.py @@ -1,6 +1,8 @@ from typing import TYPE_CHECKING, Optional +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse if TYPE_CHECKING: @@ -12,6 +14,8 @@ def start_call_extrinsic( subtensor: "Subtensor", wallet: "Wallet", netuid: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -25,6 +29,9 @@ def start_call_extrinsic( subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -43,14 +50,26 @@ def start_call_extrinsic( call = SubtensorModule(subtensor).start_call(netuid=netuid) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index 546d3b8046..5b59353d17 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -2,7 +2,9 @@ from bittensor_wallet.bittensor_wallet import Wallet +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse if TYPE_CHECKING: @@ -15,6 +17,8 @@ def set_take_extrinsic( hotkey_ss58: str, take: int, action: Literal["increase_take", "decrease_take"], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -28,6 +32,9 @@ def set_take_extrinsic( hotkey_ss58: SS58 address of the hotkey to set take for. take: The percentage of rewards that the delegate claims from nominators. action: The call function to use to set the take. Can be either "increase_take" or "decrease_take". + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -55,14 +62,26 @@ def set_take_extrinsic( else: raise ValueError(f"Invalid action: {action}") - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index 7a1cb921c8..ab796586c9 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -1,8 +1,13 @@ from typing import TYPE_CHECKING, Optional +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Balances from bittensor.core.extrinsics.utils import get_transfer_fn_params -from bittensor.core.settings import NETWORK_EXPLORER_MAP, DEFAULT_NETWORK +from bittensor.core.settings import ( + DEFAULT_MEV_PROTECTION, + NETWORK_EXPLORER_MAP, + DEFAULT_NETWORK, +) from bittensor.core.types import ExtrinsicResponse from bittensor.utils import ( get_explorer_url_for_network, @@ -23,6 +28,8 @@ def transfer_extrinsic( amount: Optional[Balance], keep_alive: bool = True, transfer_all: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -37,6 +44,9 @@ def transfer_extrinsic( amount: Amount to stake as Bittensor balance. `None` if transferring all. transfer_all: Whether to transfer all funds from this wallet to the destination address. keep_alive: If set, keeps the account alive by keeping the balance above the existential deposit. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -101,14 +111,26 @@ def transfer_extrinsic( call = getattr(Balances(subtensor), call_function)(**call_params) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) response.transaction_tao_fee = fee if response.success: diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index 4fc091ffe8..3088b8b061 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -3,8 +3,10 @@ from async_substrate_interface.errors import SubstrateRequestException from bittensor.core.errors import BalanceTypeError +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule from bittensor.core.extrinsics.utils import get_old_stakes +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse, UIDs from bittensor.utils import format_error_message from bittensor.utils.balance import Balance @@ -24,6 +26,8 @@ def unstake_extrinsic( allow_partial_stake: bool = False, rate_tolerance: float = 0.005, safe_unstaking: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -41,6 +45,9 @@ def unstake_extrinsic( allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded. rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%). safe_unstaking: If true, enables price safety checks. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -118,16 +125,28 @@ def unstake_extrinsic( logging.debug(logging_message) block_before = subtensor.block - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - nonce_key="coldkeypub", - use_nonce=True, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + nonce_key="coldkeypub", + use_nonce=True, + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = subtensor.sim_swap( @@ -184,6 +203,8 @@ def unstake_all_extrinsic( netuid: int, hotkey_ss58: str, rate_tolerance: Optional[float] = 0.005, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -198,6 +219,9 @@ def unstake_all_extrinsic( hotkey_ss58: The SS58 address of the hotkey to unstake from. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. If not passed (None), then unstaking goes without price limit. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -223,16 +247,28 @@ def unstake_all_extrinsic( limit_price=limit_price, ) - return subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - nonce_key="coldkeypub", - use_nonce=True, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + nonce_key="coldkeypub", + use_nonce=True, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -246,6 +282,8 @@ def unstake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, rate_tolerance: Optional[float] = 0.05, unstake_all: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -262,6 +300,9 @@ def unstake_multiple_extrinsic( amounts: List of amounts to unstake. If ``None``, unstake all. rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%). unstake_all: If true, unstakes all tokens. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -393,6 +434,7 @@ def unstake_multiple_extrinsic( hotkey_ss58=hotkey_ss58, netuid=netuid, rate_tolerance=rate_tolerance, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -405,6 +447,7 @@ def unstake_multiple_extrinsic( netuid=netuid, hotkey_ss58=hotkey_ss58, amount=unstaking_balance, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, diff --git a/bittensor/core/extrinsics/weights.py b/bittensor/core/extrinsics/weights.py index cae837a1e8..cb84ed69c8 100644 --- a/bittensor/core/extrinsics/weights.py +++ b/bittensor/core/extrinsics/weights.py @@ -4,8 +4,9 @@ from bittensor_drand import get_encrypted_commit +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule -from bittensor.core.settings import version_as_int +from bittensor.core.settings import DEFAULT_MEV_PROTECTION, version_as_int from bittensor.core.types import ExtrinsicResponse, Salt, UIDs, Weights from bittensor.utils import get_mechid_storage_index from bittensor.utils.btlogging import logging @@ -29,6 +30,8 @@ def commit_timelocked_weights_extrinsic( block_time: Union[int, float], commit_reveal_version: int = 4, version_key: int = version_as_int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -47,6 +50,9 @@ def commit_timelocked_weights_extrinsic( block_time: The number of seconds for block duration. commit_reveal_version: The version of the commit-reveal in the chain. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -99,17 +105,29 @@ def commit_timelocked_weights_extrinsic( commit_reveal_version=commit_reveal_version, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - use_nonce=True, - period=period, - sign_with=signing_keypair, - nonce_key=signing_keypair, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, + sign_with=signing_keypair, + nonce_key=signing_keypair, + raise_error=raise_error, + ) if response.success: logging.debug(response.message) @@ -135,6 +153,8 @@ def commit_weights_extrinsic( weights: Weights, salt: Salt, version_key: int = version_as_int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -151,6 +171,9 @@ def commit_weights_extrinsic( weights: NumPy array of weight values corresponding to each UID. salt: list of randomly generated integers as salt to generated weighted hash. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -187,17 +210,29 @@ def commit_weights_extrinsic( commit_hash=commit_hash, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - use_nonce=True, - period=period, - sign_with=signing_keypair, - nonce_key=signing_keypair, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, + sign_with=signing_keypair, + nonce_key=signing_keypair, + raise_error=raise_error, + ) if response.success: logging.debug(response.message) @@ -219,6 +254,8 @@ def reveal_weights_extrinsic( weights: Weights, salt: Salt, version_key: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -236,6 +273,9 @@ def reveal_weights_extrinsic( weights: List of weight values corresponding to each UID. salt: List of salt values corresponding to the hash function. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -267,17 +307,29 @@ def reveal_weights_extrinsic( version_key=version_key, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - use_nonce=True, - period=period, - sign_with=signing_keypair, - nonce_key=signing_keypair, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, + sign_with=signing_keypair, + nonce_key=signing_keypair, + raise_error=raise_error, + ) if response.success: logging.debug(response.message) @@ -298,6 +350,8 @@ def set_weights_extrinsic( uids: UIDs, weights: Weights, version_key: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -314,6 +368,9 @@ def set_weights_extrinsic( uids: List of neuron UIDs for which weights are being revealed. weights: List of weight values corresponding to each UID. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -344,17 +401,29 @@ def set_weights_extrinsic( version_key=version_key, ) - response = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - use_nonce=True, - nonce_key=signing_keypair, - sign_with=signing_keypair, - raise_error=raise_error, - ) + if mev_protection: + response = submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + use_nonce=True, + nonce_key=signing_keypair, + sign_with=signing_keypair, + raise_error=raise_error, + ) if response.success: logging.debug(response.message) diff --git a/bittensor/core/settings.py b/bittensor/core/settings.py index b9a1c08e07..03bf071765 100644 --- a/bittensor/core/settings.py +++ b/bittensor/core/settings.py @@ -70,6 +70,10 @@ # details https://paritytech.github.io/polkadot-sdk/master/src/sp_runtime/generic/era.rs.html#65-72 DEFAULT_PERIOD = 128 +# Default MEV Shield protection setting for extrinsics. +# When enabled, transactions are encrypted to protect against Miner Extractable Value (MEV) attacks. +DEFAULT_MEV_PROTECTION = False + # Block Explorers map network to explorer url # Must all be polkadotjs explorer urls NETWORK_EXPLORER_MAP = { From c14ad40e6b1c1fee1edea9ca1326065c15aa716b Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 18:04:54 -0800 Subject: [PATCH 39/67] update unit tests --- tests/e2e_tests/test_mev_shield.py | 2 +- tests/unit_tests/extrinsics/test_crowdloan.py | 7 +++ tests/unit_tests/extrinsics/test_proxy.py | 15 ++++++ tests/unit_tests/extrinsics/test_staking.py | 4 ++ tests/unit_tests/extrinsics/test_transfer.py | 6 +++ tests/unit_tests/extrinsics/test_unstaking.py | 5 ++ tests/unit_tests/test_subtensor.py | 47 +++++++++++++++++-- 7 files changed, 82 insertions(+), 4 deletions(-) diff --git a/tests/e2e_tests/test_mev_shield.py b/tests/e2e_tests/test_mev_shield.py index d54934599d..ac5ff1c12c 100644 --- a/tests/e2e_tests/test_mev_shield.py +++ b/tests/e2e_tests/test_mev_shield.py @@ -18,7 +18,7 @@ TEMPO_TO_SET = 3 -@pytest.mark.parametrize("local_chain", [False], indirect=True) +# @pytest.mark.parametrize("local_chain", [False], indirect=True) def test_mev_shield_happy_path( subtensor, alice_wallet, bob_wallet, charlie_wallet, dave_wallet, local_chain ): diff --git a/tests/unit_tests/extrinsics/test_crowdloan.py b/tests/unit_tests/extrinsics/test_crowdloan.py index 49bdb592c7..2427b2b920 100644 --- a/tests/unit_tests/extrinsics/test_crowdloan.py +++ b/tests/unit_tests/extrinsics/test_crowdloan.py @@ -2,6 +2,7 @@ from scalecodec.types import GenericCall import pytest from bittensor.core.extrinsics import crowdloan +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -26,6 +27,7 @@ def test_contribute_crowdloan_extrinsic(subtensor, mocker): wallet=faked_wallet, crowdloan_id=fake_crowdloan_id, amount=fake_amount, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Assertions @@ -75,6 +77,7 @@ def test_create_crowdloan_extrinsic(subtensor, mocker): end=fake_end, call=fake_call, target_address=fake_target_address, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Assertions @@ -128,6 +131,7 @@ def test_same_params_extrinsics(subtensor, mocker, extrinsic, subtensor_function subtensor=subtensor, wallet=faked_wallet, crowdloan_id=fake_crowdloan_id, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Assertions @@ -165,6 +169,7 @@ def test_update_cap_crowdloan_extrinsic(subtensor, mocker): wallet=faked_wallet, crowdloan_id=fake_crowdloan_id, new_cap=fake_new_cap, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Assertions @@ -204,6 +209,7 @@ def test_update_end_crowdloan_extrinsic(subtensor, mocker): wallet=faked_wallet, crowdloan_id=fake_crowdloan_id, new_end=fake_new_end, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Assertions @@ -247,6 +253,7 @@ def test_update_min_contribution_crowdloan_extrinsic(subtensor, mocker): wallet=faked_wallet, crowdloan_id=fake_crowdloan_id, new_min_contribution=fake_new_min_contribution, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Assertions diff --git a/tests/unit_tests/extrinsics/test_proxy.py b/tests/unit_tests/extrinsics/test_proxy.py index 5313538b45..b718d86d65 100644 --- a/tests/unit_tests/extrinsics/test_proxy.py +++ b/tests/unit_tests/extrinsics/test_proxy.py @@ -2,6 +2,7 @@ from scalecodec.types import GenericCall from bittensor.core.extrinsics import proxy +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse @@ -26,6 +27,7 @@ def test_add_proxy_extrinsic(subtensor, mocker): delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -67,6 +69,7 @@ def test_remove_proxy_extrinsic(subtensor, mocker): delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -101,6 +104,7 @@ def test_remove_proxies_extrinsic(subtensor, mocker): response = proxy.remove_proxies_extrinsic( subtensor=subtensor, wallet=wallet, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -160,6 +164,7 @@ def test_create_pure_proxy_extrinsic(subtensor, mocker): proxy_type=proxy_type, delay=delay, index=index, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -216,6 +221,7 @@ def test_kill_pure_proxy_extrinsic(subtensor, mocker): index=index, height=height, ext_index=ext_index, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -233,6 +239,7 @@ def test_kill_pure_proxy_extrinsic(subtensor, mocker): real_account_ss58=pure_proxy_ss58, force_proxy_type=proxy.ProxyType.Any, call=mocked_pallet_call.return_value, + mev_protection=DEFAULT_MEV_PROTECTION, period=None, raise_error=False, wait_for_inclusion=True, @@ -262,6 +269,7 @@ def test_proxy_extrinsic(subtensor, mocker): real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -302,6 +310,7 @@ def test_proxy_extrinsic_with_none_force_proxy_type(subtensor, mocker): real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -344,6 +353,7 @@ def test_proxy_announced_extrinsic(subtensor, mocker): real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -387,6 +397,7 @@ def test_proxy_announced_extrinsic_with_none_force_proxy_type(subtensor, mocker) real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -425,6 +436,7 @@ def test_announce_extrinsic(subtensor, mocker): wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -461,6 +473,7 @@ def test_reject_announcement_extrinsic(subtensor, mocker): wallet=wallet, delegate_ss58=delegate_ss58, call_hash=call_hash, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -497,6 +510,7 @@ def test_remove_announcement_extrinsic(subtensor, mocker): wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -529,6 +543,7 @@ def test_poke_deposit_extrinsic(subtensor, mocker): response = proxy.poke_deposit_extrinsic( subtensor=subtensor, wallet=wallet, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts diff --git a/tests/unit_tests/extrinsics/test_staking.py b/tests/unit_tests/extrinsics/test_staking.py index 8696677320..a4c69edd09 100644 --- a/tests/unit_tests/extrinsics/test_staking.py +++ b/tests/unit_tests/extrinsics/test_staking.py @@ -1,6 +1,7 @@ import pytest from bittensor.core.extrinsics import staking +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.utils.balance import Balance from bittensor.core.types import ExtrinsicResponse @@ -37,6 +38,7 @@ def test_add_stake_extrinsic(mocker): hotkey_ss58=hotkey_ss58, netuid=fake_netuid, amount=amount, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) @@ -95,6 +97,7 @@ def test_add_stake_multiple_extrinsic(subtensor, mocker, fake_wallet): netuids=netuids, hotkey_ss58s=hotkey_ss58s, amounts=amounts, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, raise_error=True, @@ -137,6 +140,7 @@ def test_set_auto_stake_extrinsic( wallet=fake_wallet, hotkey_ss58=hotkey_ss58, netuid=netuid, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts diff --git a/tests/unit_tests/extrinsics/test_transfer.py b/tests/unit_tests/extrinsics/test_transfer.py index d582d785e9..e6f560c388 100644 --- a/tests/unit_tests/extrinsics/test_transfer.py +++ b/tests/unit_tests/extrinsics/test_transfer.py @@ -1,5 +1,6 @@ import pytest from bittensor.core.extrinsics import transfer +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.utils.balance import Balance from bittensor.core.types import ExtrinsicResponse @@ -47,6 +48,7 @@ def test_transfer_extrinsic_success(subtensor, fake_wallet, mocker): destination_ss58=fake_destination, amount=fake_amount, transfer_all=False, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, keep_alive=True, @@ -118,6 +120,7 @@ def test_transfer_extrinsic_call_successful_with_failed_response( destination_ss58=fake_destination, amount=fake_amount, transfer_all=False, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, keep_alive=True, @@ -183,6 +186,7 @@ def test_transfer_extrinsic_insufficient_balance(subtensor, fake_wallet, mocker) destination_ss58=fake_destination, amount=fake_amount, transfer_all=False, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, keep_alive=True, @@ -224,6 +228,7 @@ def test_transfer_extrinsic_invalid_destination(subtensor, fake_wallet, mocker): destination_ss58=fake_destination, amount=fake_amount, transfer_all=False, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, keep_alive=True, @@ -255,6 +260,7 @@ def test_transfer_extrinsic_unlock_key_false(subtensor, fake_wallet, mocker): destination_ss58=fake_destination, amount=fake_amount, transfer_all=False, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, keep_alive=True, diff --git a/tests/unit_tests/extrinsics/test_unstaking.py b/tests/unit_tests/extrinsics/test_unstaking.py index 92f1192954..9611939128 100644 --- a/tests/unit_tests/extrinsics/test_unstaking.py +++ b/tests/unit_tests/extrinsics/test_unstaking.py @@ -1,4 +1,5 @@ from bittensor.core.extrinsics import unstaking +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -33,6 +34,7 @@ def test_unstake_extrinsic(fake_wallet, mocker): hotkey_ss58=hotkey_ss58, netuid=fake_netuid, amount=amount, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) @@ -79,6 +81,7 @@ def test_unstake_all_extrinsic(fake_wallet, mocker): wallet=fake_wallet, hotkey_ss58=hotkey, netuid=fake_netuid, + mev_protection=DEFAULT_MEV_PROTECTION, ) # Asserts @@ -139,6 +142,7 @@ def test_unstake_multiple_extrinsic(subtensor, fake_wallet, mocker): hotkey_ss58s=hotkey_ss58s, netuids=fake_netuids, amounts=amounts, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) @@ -158,6 +162,7 @@ def test_unstake_multiple_extrinsic(subtensor, fake_wallet, mocker): netuid=sn_5, hotkey_ss58="hotkey1", amount=Balance.from_tao(1.1, sn_5), + mev_protection=DEFAULT_MEV_PROTECTION, period=None, raise_error=False, wait_for_inclusion=wait_for_inclusion, diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 71da16d352..da389445fb 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -16,7 +16,11 @@ from bittensor.core.async_subtensor import AsyncSubtensor, logging from bittensor.core.axon import Axon from bittensor.core.chain_data import SubnetHyperparameters, SelectiveMetagraphIndex -from bittensor.core.settings import version_as_int, DEFAULT_PERIOD +from bittensor.core.settings import ( + DEFAULT_MEV_PROTECTION, + DEFAULT_PERIOD, + version_as_int, +) from bittensor.core.subtensor import Subtensor from bittensor.core.types import AxonServeCallParams from bittensor.core.types import ExtrinsicResponse @@ -1180,6 +1184,7 @@ def test_serve_axon(subtensor, mocker): netuid=fake_netuid, axon=fake_axon, certificate=fake_certificate, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=fake_wait_for_inclusion, @@ -1220,6 +1225,7 @@ def test_commit(subtensor, fake_wallet, mocker): netuid=fake_netuid, data_type=f"Raw{len(fake_data)}", data=fake_data.encode(), + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -1278,6 +1284,7 @@ def test_transfer(subtensor, fake_wallet, mocker): destination_ss58=fake_dest, amount=fake_amount, transfer_all=False, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, keep_alive=True, @@ -1813,6 +1820,7 @@ def test_reveal_weights(subtensor, fake_wallet, mocker): version_key=version_as_int, weights=weights, salt=salt, + mev_protection=DEFAULT_MEV_PROTECTION, period=16, raise_error=False, wait_for_inclusion=False, @@ -2647,7 +2655,7 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): rate_tolerance=0.005, period=DEFAULT_PERIOD, raise_error=False, - mev_protection=False, + mev_protection=DEFAULT_MEV_PROTECTION, ) assert result == mock_add_stake_extrinsic.return_value @@ -2691,7 +2699,7 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): rate_tolerance=fake_rate_tolerance, period=DEFAULT_PERIOD, raise_error=False, - mev_protection=False, + mev_protection=DEFAULT_MEV_PROTECTION, ) assert result == mock_add_stake_extrinsic.return_value @@ -2723,6 +2731,7 @@ def test_add_stake_multiple_success(mocker, fake_wallet, subtensor): hotkey_ss58s=fake_hotkey_ss58, netuids=[1], amounts=fake_amount, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=False, period=DEFAULT_PERIOD, @@ -2850,6 +2859,7 @@ def test_swap_stake_success(mocker, subtensor, fake_wallet): safe_swapping=False, allow_partial_stake=False, rate_tolerance=0.005, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, ) @@ -2896,6 +2906,7 @@ def test_swap_stake_with_safe_staking(mocker, subtensor, fake_wallet): safe_swapping=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, ) @@ -2931,6 +2942,7 @@ def test_unstake_multiple_success(mocker, subtensor, fake_wallet): amounts=fake_amounts, wait_for_inclusion=True, wait_for_finalization=False, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, unstake_all=False, raise_error=False, @@ -2982,6 +2994,7 @@ def test_set_weights_with_commit_reveal_enabled(subtensor, fake_wallet, mocker): weights=fake_weights, commit_reveal_version=4, version_key=subtensor_module.version_as_int, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, block_time=12.0, @@ -3044,6 +3057,7 @@ def test_set_subnet_identity(mocker, subtensor, fake_wallet): discord=fake_subnet_identity.discord, description=fake_subnet_identity.description, additional=fake_subnet_identity.additional, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_finalization=True, @@ -3143,6 +3157,7 @@ def test_start_call(subtensor, mocker): subtensor=subtensor, wallet=wallet_name, netuid=netuid, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=False, period=DEFAULT_PERIOD, @@ -3703,6 +3718,7 @@ def test_set_children(subtensor, fake_wallet, mocker): hotkey_ss58=fake_wallet.hotkey.ss58_address, netuid=fake_netuid, children=fake_children, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_finalization=True, wait_for_inclusion=True, raise_error=False, @@ -3731,6 +3747,7 @@ def test_unstake_all(subtensor, fake_wallet, mocker): hotkey_ss58=fake_wallet.hotkey.ss58_address, netuid=1, rate_tolerance=0.005, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, period=DEFAULT_PERIOD, @@ -3881,6 +3898,7 @@ def test_add_liquidity(subtensor, fake_wallet, mocker): price_low=Balance.from_tao(180).rao, price_high=Balance.from_tao(130).rao, hotkey_ss58=None, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, period=DEFAULT_PERIOD, @@ -3914,6 +3932,7 @@ def test_modify_liquidity(subtensor, fake_wallet, mocker): position_id=position_id, liquidity_delta=Balance.from_tao(150), hotkey_ss58=None, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, period=DEFAULT_PERIOD, @@ -3945,6 +3964,7 @@ def test_remove_liquidity(subtensor, fake_wallet, mocker): netuid=netuid, position_id=position_id, hotkey_ss58=None, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, period=DEFAULT_PERIOD, @@ -3975,6 +3995,7 @@ def test_toggle_user_liquidity(subtensor, fake_wallet, mocker): wallet=fake_wallet, netuid=netuid, enable=enable, + mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=True, wait_for_finalization=True, period=DEFAULT_PERIOD, @@ -4430,6 +4451,7 @@ def test_set_auto_stake(subtensor, mocker): wallet=wallet, netuid=netuid, hotkey_ss58=hotkey, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4511,6 +4533,7 @@ def test_contribute_crowdloan(mocker, subtensor): wallet=wallet, crowdloan_id=crowdloan_id, amount=amount, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4555,6 +4578,7 @@ def test_create_crowdloan(mocker, subtensor): end=end, call=call, target_address=target_address, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4593,6 +4617,7 @@ def test_crowdloan_methods_with_crowdloan_id_parameter( subtensor=subtensor, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4625,6 +4650,7 @@ def test_update_cap_crowdloan(mocker, subtensor): wallet=wallet, crowdloan_id=crowdloan_id, new_cap=new_cap, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4657,6 +4683,7 @@ def test_update_end_crowdloan(mocker, subtensor): wallet=wallet, crowdloan_id=crowdloan_id, new_end=new_end, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4689,6 +4716,7 @@ def test_update_min_contribution_crowdloan(mocker, subtensor): wallet=wallet, crowdloan_id=crowdloan_id, new_min_contribution=new_min_contribution, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5096,6 +5124,7 @@ def test_claim_root(mocker, subtensor): subtensor=subtensor, wallet=wallet, netuids=netuids, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5123,6 +5152,7 @@ def test_set_root_claim_type(mocker, subtensor): subtensor=subtensor, wallet=faked_wallet, new_root_claim_type=fake_new_root_claim_type, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5446,6 +5476,7 @@ def test_add_proxy(mocker, subtensor): delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5477,6 +5508,7 @@ def test_announce_proxy(mocker, subtensor): wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5511,6 +5543,7 @@ def test_create_pure_proxy(mocker, subtensor): proxy_type=proxy_type, delay=delay, index=index, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5555,6 +5588,7 @@ def test_kill_pure_proxy(mocker, subtensor): height=height, ext_index=ext_index, force_proxy_type=subtensor_module.ProxyType.Any, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5578,6 +5612,7 @@ def test_poke_deposit(mocker, subtensor): mocked_poke_deposit_extrinsic.assert_called_once_with( subtensor=subtensor, wallet=wallet, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5610,6 +5645,7 @@ def test_proxy(mocker, subtensor): real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5647,6 +5683,7 @@ def test_proxy_announced(mocker, subtensor): real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5678,6 +5715,7 @@ def test_reject_proxy_announcement(mocker, subtensor): wallet=wallet, delegate_ss58=delegate_ss58, call_hash=call_hash, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5709,6 +5747,7 @@ def test_remove_proxy_announcement(mocker, subtensor): wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5732,6 +5771,7 @@ def test_remove_proxies(mocker, subtensor): mocked_remove_proxies_extrinsic.assert_called_once_with( subtensor=subtensor, wallet=wallet, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5766,6 +5806,7 @@ def test_remove_proxy(mocker, subtensor): delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, From cf2d394636074f54345b72f3bde4a5cf813c87e3 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 18:05:29 -0800 Subject: [PATCH 40/67] add `mev_protection` with default value to subtensor methods --- bittensor/core/subtensor.py | 303 +++++++++++++++++++++++++++++++++++- 1 file changed, 302 insertions(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 5c0e3ba258..92646c7b21 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -124,6 +124,7 @@ from bittensor.core.metagraph import Metagraph from bittensor.core.settings import ( version_as_int, + DEFAULT_MEV_PROTECTION, DEFAULT_PERIOD, TAO_APP_BLOCK_EXPLORER, TYPE_REGISTRY, @@ -4533,8 +4534,8 @@ def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - mev_protection: bool = False, *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4598,6 +4599,8 @@ def add_liquidity( price_low: Balance, price_high: Balance, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4613,6 +4616,9 @@ def add_liquidity( price_low: The lower bound of the price tick range. In TAO. price_high: The upper bound of the price tick range. In TAO. hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4634,6 +4640,7 @@ def add_liquidity( price_low=price_low, price_high=price_high, hotkey_ss58=hotkey_ss58, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4646,6 +4653,8 @@ def add_stake_multiple( netuids: UIDs, hotkey_ss58s: list[str], amounts: list[Balance], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4660,6 +4669,9 @@ def add_stake_multiple( netuids: List of subnet UIDs. hotkey_ss58s: List of ``SS58`` addresses of hotkeys to stake to. amounts: List of corresponding TAO amounts to bet for each netuid and hotkey. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4679,6 +4691,7 @@ def add_stake_multiple( netuids=netuids, hotkey_ss58s=hotkey_ss58s, amounts=amounts, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4691,6 +4704,8 @@ def add_proxy( delegate_ss58: str, proxy_type: Union[str, "ProxyType"], delay: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4709,6 +4724,9 @@ def add_proxy( proxy_type: The type of proxy permissions (e.g., "Any", "NonTransfer", "Governance", "Staking"). Can be a string or ProxyType enum value. delay: The number of blocks before the proxy can be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4729,6 +4747,7 @@ def add_proxy( delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4740,6 +4759,8 @@ def announce_proxy( wallet: "Wallet", real_account_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4756,6 +4777,9 @@ def announce_proxy( wallet: Bittensor wallet object (should be the proxy account wallet). real_account_ss58: The SS58 address of the real account on whose behalf the call will be made. call_hash: The hash of the call that will be executed in the future. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4775,6 +4799,7 @@ def announce_proxy( wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4785,6 +4810,8 @@ def burned_register( self, wallet: "Wallet", netuid: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4797,6 +4824,9 @@ def burned_register( Parameters: wallet: The wallet associated with the neuron to be registered. netuid: The unique identifier of the subnet. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4812,6 +4842,7 @@ def burned_register( return root_register_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4822,6 +4853,7 @@ def burned_register( subtensor=self, wallet=wallet, netuid=netuid, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4832,6 +4864,8 @@ def claim_root( self, wallet: "Wallet", netuids: "UIDs", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4842,6 +4876,9 @@ def claim_root( Parameters: wallet: Bittensor Wallet instance. netuids: The netuids to claim root emissions for. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4856,6 +4893,7 @@ def claim_root( subtensor=self, wallet=wallet, netuids=netuids, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4872,6 +4910,8 @@ def commit_weights( mechid: int = 0, version_key: int = version_as_int, max_attempts: int = 5, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = 16, raise_error: bool = True, wait_for_inclusion: bool = False, @@ -4890,6 +4930,9 @@ def commit_weights( mechid: Subnet mechanism unique identifier. version_key: Version key for compatibility with the network. max_attempts: The number of maximum attempts to commit weights. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4925,6 +4968,7 @@ def commit_weights( uids=uids, weights=weights, salt=salt, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4948,6 +4992,8 @@ def contribute_crowdloan( wallet: "Wallet", crowdloan_id: int, amount: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4960,6 +5006,9 @@ def contribute_crowdloan( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to contribute to. amount: Amount to contribute. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4975,6 +5024,7 @@ def contribute_crowdloan( wallet=wallet, crowdloan_id=crowdloan_id, amount=amount, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4990,6 +5040,8 @@ def create_crowdloan( end: int, call: Optional["GenericCall"] = None, target_address: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5006,6 +5058,9 @@ def create_crowdloan( end: Block number when the campaign ends. call: Runtime call data (e.g., subtensor::register_leased_network). target_address: SS58 address to transfer funds to on success. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5025,6 +5080,7 @@ def create_crowdloan( end=end, call=call, target_address=target_address, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5037,6 +5093,8 @@ def create_pure_proxy( proxy_type: Union[str, "ProxyType"], delay: int, index: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5054,6 +5112,9 @@ def create_pure_proxy( proxy_type: The type of proxy permissions for the pure proxy. Can be a string or ProxyType enum value. delay: The number of blocks before the pure proxy can be used. index: The index to use for generating the pure proxy account address. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5075,6 +5136,7 @@ def create_pure_proxy( proxy_type=proxy_type, delay=delay, index=index, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5085,6 +5147,8 @@ def dissolve_crowdloan( self, wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5099,6 +5163,9 @@ def dissolve_crowdloan( Parameters: wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to dissolve. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5119,6 +5186,7 @@ def dissolve_crowdloan( subtensor=self, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5129,6 +5197,8 @@ def finalize_crowdloan( self, wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5142,6 +5212,9 @@ def finalize_crowdloan( Parameters: wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to finalize. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. @@ -5154,6 +5227,7 @@ def finalize_crowdloan( subtensor=self, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5170,6 +5244,8 @@ def kill_pure_proxy( height: int, ext_index: int, force_proxy_type: Optional[Union[str, "ProxyType"]] = ProxyType.Any, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5202,6 +5278,9 @@ def kill_pure_proxy( type (or `Any`) with the pure proxy account. Defaults to `ProxyType.Any` for maximum compatibility. If `None`, Substrate will automatically select an available proxy type from the spawner's proxy relationships. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5231,6 +5310,7 @@ def kill_pure_proxy( height=height, ext_index=ext_index, force_proxy_type=force_proxy_type, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5246,6 +5326,8 @@ def mev_submit_encrypted( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, + blocks_for_revealed_execution: int = 5, ) -> ExtrinsicResponse: """ Submits an encrypted extrinsic to the MEV Shield pallet. @@ -5263,6 +5345,12 @@ def mev_submit_encrypted( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the DecryptedExecuted event, indicating that validators + have successfully decrypted and executed the inner call. If True, the function will poll subsequent + blocks for the event matching this submission's commitment. + blocks_for_revealed_execution: Maximum number of blocks to poll for the DecryptedExecuted event after + inclusion. The function checks blocks from start_block+1 to start_block + blocks_for_revealed_execution. + Returns immediately if the event is found before the block limit is reached. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5286,6 +5374,8 @@ def mev_submit_encrypted( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, + blocks_for_revealed_execution=blocks_for_revealed_execution, ) def modify_liquidity( @@ -5295,6 +5385,8 @@ def modify_liquidity( position_id: int, liquidity_delta: Balance, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5308,6 +5400,9 @@ def modify_liquidity( position_id: The id of the position record in the pool. liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative). hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5354,6 +5449,7 @@ def modify_liquidity( position_id=position_id, liquidity_delta=liquidity_delta, hotkey_ss58=hotkey_ss58, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5369,6 +5465,8 @@ def move_stake( destination_hotkey_ss58: str, amount: Optional[Balance] = None, move_all_stake: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5385,6 +5483,9 @@ def move_stake( destination_hotkey_ss58: The SS58 address of the destination hotkey. amount: Amount of stake to move. move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5405,6 +5506,7 @@ def move_stake( destination_hotkey_ss58=destination_hotkey_ss58, amount=amount, move_all_stake=move_all_stake, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5414,6 +5516,8 @@ def move_stake( def poke_deposit( self, wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5428,6 +5532,9 @@ def poke_deposit( Parameters: wallet: Bittensor wallet object (the account whose deposits will be adjusted). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. @@ -5448,6 +5555,7 @@ def poke_deposit( return poke_deposit_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5460,6 +5568,8 @@ def proxy( real_account_ss58: str, force_proxy_type: Optional[Union[str, "ProxyType"]], call: "GenericCall", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5478,6 +5588,9 @@ def proxy( force_proxy_type: The type of proxy to use for the call. If None, any proxy type can be used. Otherwise, must match one of the allowed proxy types. Can be a string or ProxyType enum value. call: The inner call to be executed on behalf of the real account. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5498,6 +5611,7 @@ def proxy( real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5511,6 +5625,8 @@ def proxy_announced( real_account_ss58: str, force_proxy_type: Optional[Union[str, "ProxyType"]], call: "GenericCall", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5530,6 +5646,9 @@ def proxy_announced( force_proxy_type: The type of proxy to use for the call. If None, any proxy type can be used. Otherwise, must match one of the allowed proxy types. Can be a string or ProxyType enum value. call: The inner call to be executed on behalf of the real account (must match the announced call_hash). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5551,6 +5670,7 @@ def proxy_announced( real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5561,6 +5681,8 @@ def refund_crowdloan( self, wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5575,6 +5697,9 @@ def refund_crowdloan( Parameters: wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to refund. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5595,6 +5720,7 @@ def refund_crowdloan( subtensor=self, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5606,6 +5732,8 @@ def reject_proxy_announcement( wallet: "Wallet", delegate_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5622,6 +5750,9 @@ def reject_proxy_announcement( wallet: Bittensor wallet object (should be the real account wallet). delegate_ss58: The SS58 address of the delegate proxy account whose announcement is being rejected. call_hash: The hash of the call that was announced and is now being rejected. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5640,6 +5771,7 @@ def reject_proxy_announcement( wallet=wallet, delegate_ss58=delegate_ss58, call_hash=call_hash, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5658,6 +5790,8 @@ def register( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5681,6 +5815,9 @@ def register( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If ``true``, the registration process will log more information. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5706,6 +5843,7 @@ def register( dev_id=dev_id, output_in_place=output_in_place, log_verbose=log_verbose, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5715,6 +5853,8 @@ def register( def register_subnet( self, wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5725,6 +5865,9 @@ def register_subnet( Parameters: wallet: The wallet to be used for subnet registration. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5738,6 +5881,7 @@ def register_subnet( return register_subnet_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5749,6 +5893,8 @@ def remove_proxy_announcement( wallet: "Wallet", real_account_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5765,6 +5911,9 @@ def remove_proxy_announcement( wallet: Bittensor wallet object (should be the proxy account wallet that made the announcement). real_account_ss58: The SS58 address of the real account on whose behalf the call was announced. call_hash: The hash of the call that was announced and is now being removed. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5784,6 +5933,7 @@ def remove_proxy_announcement( wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5796,6 +5946,8 @@ def remove_liquidity( netuid: int, position_id: int, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5808,6 +5960,9 @@ def remove_liquidity( netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5829,6 +5984,7 @@ def remove_liquidity( netuid=netuid, position_id=position_id, hotkey_ss58=hotkey_ss58, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5838,6 +5994,8 @@ def remove_liquidity( def remove_proxies( self, wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5853,6 +6011,9 @@ def remove_proxies( Parameters: wallet: Bittensor wallet object. The account whose proxies will be removed (the delegator). All proxy relationships where wallet.coldkey.ss58_address is the real account will be removed. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5870,6 +6031,7 @@ def remove_proxies( return remove_proxies_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5882,6 +6044,8 @@ def remove_proxy( delegate_ss58: str, proxy_type: Union[str, "ProxyType"], delay: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5899,6 +6063,9 @@ def remove_proxy( delegate_ss58: The SS58 address of the delegate proxy account to remove. proxy_type: The type of proxy permissions to remove. Can be a string or ProxyType enum value. delay: The number of blocks before the proxy removal takes effect. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5919,6 +6086,7 @@ def remove_proxy( delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5935,6 +6103,8 @@ def reveal_weights( mechid: int = 0, max_attempts: int = 5, version_key: int = version_as_int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = 16, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5953,6 +6123,9 @@ def reveal_weights( mechid: The subnet mechanism unique identifier. max_attempts: The number of maximum attempts to reveal weights. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5985,6 +6158,7 @@ def reveal_weights( weights=weights, salt=salt, version_key=version_key, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6003,6 +6177,8 @@ def reveal_weights( def root_register( self, wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6013,6 +6189,9 @@ def root_register( Parameters: wallet (bittensor_wallet.Wallet): Bittensor wallet instance. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6027,6 +6206,7 @@ def root_register( return root_register_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6037,6 +6217,8 @@ def root_set_pending_childkey_cooldown( self, wallet: "Wallet", cooldown: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6047,6 +6229,9 @@ def root_set_pending_childkey_cooldown( Parameters: wallet: bittensor wallet instance. cooldown: the number of blocks to setting pending childkey cooldown. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6063,6 +6248,7 @@ def root_set_pending_childkey_cooldown( subtensor=self, wallet=wallet, cooldown=cooldown, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6074,6 +6260,8 @@ def set_auto_stake( wallet: "Wallet", netuid: int, hotkey_ss58: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6086,6 +6274,9 @@ def set_auto_stake( netuid: The subnet unique identifier. hotkey_ss58: The SS58 address of the validator's hotkey to which the miner automatically stakes all rewards received from the specified subnet immediately upon receipt. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6104,6 +6295,7 @@ def set_auto_stake( wallet=wallet, netuid=netuid, hotkey_ss58=hotkey_ss58, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6116,6 +6308,8 @@ def set_children( netuid: int, hotkey_ss58: str, children: list[tuple[float, str]], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6129,6 +6323,9 @@ def set_children( hotkey_ss58: The ``SS58`` address of the neuron's hotkey. netuid: The netuid value. children: A list of children with their proportions. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6145,6 +6342,7 @@ def set_children( hotkey_ss58=hotkey_ss58, netuid=netuid, children=children, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6227,6 +6425,8 @@ def set_root_claim_type( self, wallet: "Wallet", new_root_claim_type: "Literal['Swap', 'Keep'] | RootClaimType | dict", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6241,6 +6441,9 @@ def set_root_claim_type( - RootClaimType: RootClaimType.Swap, RootClaimType.Keep - Dict: {"KeepSubnets": {"subnets": [1, 2, 3]}} - Callable: RootClaimType.KeepSubnets([1, 2, 3]) + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6255,6 +6458,7 @@ def set_root_claim_type( subtensor=self, wallet=wallet, new_root_claim_type=new_root_claim_type, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6266,6 +6470,8 @@ def set_subnet_identity( wallet: "Wallet", netuid: int, subnet_identity: SubnetIdentity, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6279,6 +6485,9 @@ def set_subnet_identity( netuid: The unique ID of the network on which the operation takes place. subnet_identity: The identity data of the subnet including attributes like name, GitHub repository, contact, URL, discord, description, and any additional metadata. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6301,6 +6510,7 @@ def set_subnet_identity( discord=subnet_identity.discord, description=subnet_identity.description, additional=subnet_identity.additional, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6318,6 +6528,8 @@ def set_weights( commit_reveal_version: int = 4, max_attempts: int = 5, version_key: int = version_as_int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = 8, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6339,6 +6551,9 @@ def set_weights( commit_reveal_version: The version of the chain commit-reveal protocol to use. max_attempts: The number of maximum attempts to set weights. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6397,6 +6612,7 @@ def _blocks_weight_limit() -> bool: block_time=block_time, commit_reveal_version=commit_reveal_version, version_key=version_key, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6428,6 +6644,7 @@ def _blocks_weight_limit() -> bool: uids=uids, weights=weights, version_key=version_key, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6450,6 +6667,8 @@ def serve_axon( netuid: int, axon: "Axon", certificate: Optional[Certificate] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6465,6 +6684,9 @@ def serve_axon( netuid: The unique identifier of the subnetwork. axon: The Axon instance to be registered for serving. certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6483,6 +6705,7 @@ def serve_axon( netuid=netuid, axon=axon, certificate=certificate, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6494,6 +6717,8 @@ def set_commitment( wallet: "Wallet", netuid: int, data: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6510,6 +6735,9 @@ def set_commitment( wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6535,6 +6763,7 @@ def set_commitment( netuid=netuid, data_type=f"Raw{len(data)}", data=data.encode(), + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6601,6 +6830,8 @@ def start_call( self, wallet: "Wallet", netuid: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6613,6 +6844,9 @@ def start_call( Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6627,6 +6861,7 @@ def start_call( subtensor=self, wallet=wallet, netuid=netuid, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6643,6 +6878,8 @@ def swap_stake( safe_swapping: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6665,6 +6902,9 @@ def swap_stake( rate_tolerance: The maximum allowed increase in the price ratio between subnets (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when safe_staking is True. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6693,6 +6933,7 @@ def swap_stake( safe_swapping=safe_swapping, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6704,6 +6945,8 @@ def toggle_user_liquidity( wallet: "Wallet", netuid: int, enable: bool, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6715,6 +6958,9 @@ def toggle_user_liquidity( wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. enable: Boolean indicating whether to enable user liquidity. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6732,6 +6978,7 @@ def toggle_user_liquidity( wallet=wallet, netuid=netuid, enable=enable, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6745,6 +6992,8 @@ def transfer( amount: Optional[Balance], transfer_all: bool = False, keep_alive: bool = True, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6759,6 +7008,9 @@ def transfer( amount: Number of tokens to transfer. `None` is transferring all. transfer_all: Flag to transfer all tokens. keep_alive: Flag to keep the connection alive. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6777,6 +7029,7 @@ def transfer( amount=amount, transfer_all=transfer_all, keep_alive=keep_alive, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6791,6 +7044,8 @@ def transfer_stake( origin_netuid: int, destination_netuid: int, amount: Balance, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6806,6 +7061,9 @@ def transfer_stake( origin_netuid: The source subnet UID. destination_netuid: The destination subnet UID. amount: Amount to transfer. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6825,6 +7083,7 @@ def transfer_stake( origin_netuid=origin_netuid, destination_netuid=destination_netuid, amount=amount, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6840,6 +7099,8 @@ def unstake( allow_partial_stake: bool = False, rate_tolerance: float = 0.005, safe_unstaking: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6861,6 +7122,9 @@ def unstake( 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. safe_unstaking: If true, enables price safety checks to protect against fluctuating prices. The unstake will only execute if the price change doesn't exceed the rate tolerance. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6885,6 +7149,7 @@ def unstake( allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, safe_unstaking=safe_unstaking, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6897,6 +7162,8 @@ def unstake_all( netuid: int, hotkey_ss58: str, rate_tolerance: Optional[float] = 0.005, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6910,6 +7177,9 @@ def unstake_all( hotkey_ss58: The SS58 address of the hotkey to unstake from. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. If not passed (None), then unstaking goes without price limit. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6964,6 +7234,7 @@ def unstake_all( netuid=netuid, hotkey_ss58=hotkey_ss58, rate_tolerance=rate_tolerance, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6977,6 +7248,8 @@ def unstake_multiple( hotkey_ss58s: list[str], amounts: Optional[list[Balance]] = None, unstake_all: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6992,6 +7265,9 @@ def unstake_multiple( hotkey_ss58s: A list of hotkey `SS58` addresses to unstake from. amounts: The amounts of TAO to unstake from each hotkey. If not provided, unstakes all. unstake_all: If true, unstakes all tokens. If `True` amounts are ignored. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7012,6 +7288,7 @@ def unstake_multiple( hotkey_ss58s=hotkey_ss58s, amounts=amounts, unstake_all=unstake_all, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7023,6 +7300,8 @@ def update_cap_crowdloan( wallet: "Wallet", crowdloan_id: int, new_cap: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7038,6 +7317,9 @@ def update_cap_crowdloan( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_cap: The new fundraising cap (in TAO or Balance). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7058,6 +7340,7 @@ def update_cap_crowdloan( wallet=wallet, crowdloan_id=crowdloan_id, new_cap=new_cap, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7069,6 +7352,8 @@ def update_end_crowdloan( wallet: "Wallet", crowdloan_id: int, new_end: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7084,6 +7369,9 @@ def update_end_crowdloan( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_end: The new block number at which the crowdloan will end. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7105,6 +7393,7 @@ def update_end_crowdloan( wallet=wallet, crowdloan_id=crowdloan_id, new_end=new_end, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7116,6 +7405,8 @@ def update_min_contribution_crowdloan( wallet: "Wallet", crowdloan_id: int, new_min_contribution: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7131,6 +7422,9 @@ def update_min_contribution_crowdloan( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_min_contribution: The new minimum contribution amount (in TAO or Balance). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7151,6 +7445,7 @@ def update_min_contribution_crowdloan( wallet=wallet, crowdloan_id=crowdloan_id, new_min_contribution=new_min_contribution, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7161,6 +7456,8 @@ def withdraw_crowdloan( self, wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7172,6 +7469,9 @@ def withdraw_crowdloan( Parameters: wallet: Wallet instance used to sign the transaction (must be unlocked). crowdloan_id: The unique identifier of the crowdloan to withdraw from. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7190,6 +7490,7 @@ def withdraw_crowdloan( subtensor=self, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, From 4922bb6407cdbcc4b0f567309ad3023490237979 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 18:06:17 -0800 Subject: [PATCH 41/67] update async `add_stake` and `mev_submit_encrypted` methods --- bittensor/core/async_subtensor.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 3715a457ac..f6c9fc29a5 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -123,6 +123,7 @@ from bittensor.core.extrinsics.utils import get_transfer_fn_params from bittensor.core.metagraph import AsyncMetagraph from bittensor.core.settings import ( + DEFAULT_MEV_PROTECTION, version_as_int, DEFAULT_PERIOD, TYPE_REGISTRY, @@ -5810,8 +5811,8 @@ async def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - mev_protection: bool = False, *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6526,6 +6527,8 @@ async def mev_submit_encrypted( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, + blocks_for_revealed_execution: int = 5, ) -> ExtrinsicResponse: """ Submits an encrypted extrinsic to the MEV Shield pallet. @@ -6543,6 +6546,12 @@ async def mev_submit_encrypted( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the DecryptedExecuted event, indicating that validators + have successfully decrypted and executed the inner call. If True, the function will poll subsequent + blocks for the event matching this submission's commitment. + blocks_for_revealed_execution: Maximum number of blocks to poll for the DecryptedExecuted event after + inclusion. The function checks blocks from start_block+1 to start_block + blocks_for_revealed_execution. + Returns immediately if the event is found before the block limit is reached. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6566,6 +6575,8 @@ async def mev_submit_encrypted( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, + blocks_for_revealed_execution=blocks_for_revealed_execution, ) async def modify_liquidity( From 78791332a9df98bfa6e408b907956bea5d84672c Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 19:30:34 -0800 Subject: [PATCH 42/67] add `mev_protection` to all async extrinsics --- bittensor/core/extrinsics/asyncex/children.py | 68 +++- .../core/extrinsics/asyncex/crowdloan.py | 299 ++++++++++++---- .../core/extrinsics/asyncex/liquidity.py | 134 +++++-- .../core/extrinsics/asyncex/move_stake.py | 101 ++++-- bittensor/core/extrinsics/asyncex/proxy.py | 338 +++++++++++++----- .../core/extrinsics/asyncex/registration.py | 134 +++++-- bittensor/core/extrinsics/asyncex/root.py | 101 ++++-- bittensor/core/extrinsics/asyncex/serving.py | 83 +++-- bittensor/core/extrinsics/asyncex/staking.py | 39 +- .../core/extrinsics/asyncex/start_call.py | 35 +- bittensor/core/extrinsics/asyncex/take.py | 35 +- bittensor/core/extrinsics/asyncex/transfer.py | 40 ++- .../core/extrinsics/asyncex/unstaking.py | 84 +++-- bittensor/core/extrinsics/asyncex/weights.py | 163 ++++++--- 14 files changed, 1256 insertions(+), 398 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/children.py b/bittensor/core/extrinsics/asyncex/children.py index e8bc2fa222..c51d807cd4 100644 --- a/bittensor/core/extrinsics/asyncex/children.py +++ b/bittensor/core/extrinsics/asyncex/children.py @@ -1,6 +1,8 @@ from typing import TYPE_CHECKING, Optional +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule, Sudo +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils import float_to_u64 @@ -15,6 +17,8 @@ async def set_children_extrinsic( hotkey_ss58: str, netuid: int, children: list[tuple[float, str]], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -29,6 +33,9 @@ async def set_children_extrinsic( hotkey_ss58: The ``SS58`` address of the neuron's hotkey. netuid: The netuid value. children: A list of children with their proportions. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -67,14 +74,26 @@ async def set_children_extrinsic( ], ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) return response except Exception as error: @@ -85,6 +104,8 @@ async def root_set_pending_childkey_cooldown_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", cooldown: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -97,6 +118,9 @@ async def root_set_pending_childkey_cooldown_extrinsic( subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). cooldown: The cooldown period in blocks. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -119,14 +143,26 @@ async def root_set_pending_childkey_cooldown_extrinsic( sudo_call = await Sudo(subtensor).sudo(call=call) - response = await subtensor.sign_and_send_extrinsic( - call=sudo_call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=sudo_call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=sudo_call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) return response except Exception as error: diff --git a/bittensor/core/extrinsics/asyncex/crowdloan.py b/bittensor/core/extrinsics/asyncex/crowdloan.py index a5f0941986..3f5de9a3a0 100644 --- a/bittensor/core/extrinsics/asyncex/crowdloan.py +++ b/bittensor/core/extrinsics/asyncex/crowdloan.py @@ -1,6 +1,8 @@ from typing import TYPE_CHECKING, Optional +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Crowdloan +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import check_balance_amount @@ -16,6 +18,8 @@ async def contribute_crowdloan_extrinsic( wallet: "Wallet", crowdloan_id: int, amount: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -29,6 +33,9 @@ async def contribute_crowdloan_extrinsic( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to contribute to. amount: Amount to contribute. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -52,14 +59,26 @@ async def contribute_crowdloan_extrinsic( amount=amount.rao, ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -74,6 +93,8 @@ async def create_crowdloan_extrinsic( end: int, call: Optional["GenericCall"] = None, target_address: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -91,6 +112,9 @@ async def create_crowdloan_extrinsic( end: Block number when the campaign ends. call: Runtime call data (e.g., subtensor::register_leased_network). target_address: SS58 address to transfer funds to on success. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -120,14 +144,26 @@ async def create_crowdloan_extrinsic( target_address=target_address, ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -137,6 +173,8 @@ async def dissolve_crowdloan_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -152,6 +190,9 @@ async def dissolve_crowdloan_extrinsic( subtensor: Active Subtensor connection. wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to dissolve. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -176,14 +217,26 @@ async def dissolve_crowdloan_extrinsic( call = await Crowdloan(subtensor).dissolve(crowdloan_id=crowdloan_id) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -193,6 +246,8 @@ async def finalize_crowdloan_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -207,6 +262,9 @@ async def finalize_crowdloan_extrinsic( subtensor: Active Subtensor connection. wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to finalize. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. @@ -223,14 +281,26 @@ async def finalize_crowdloan_extrinsic( call = await Crowdloan(subtensor).finalize(crowdloan_id=crowdloan_id) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -240,6 +310,8 @@ async def refund_crowdloan_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -255,6 +327,9 @@ async def refund_crowdloan_extrinsic( subtensor: Active Subtensor connection. wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to refund. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -279,14 +354,26 @@ async def refund_crowdloan_extrinsic( call = await Crowdloan(subtensor).refund(crowdloan_id=crowdloan_id) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -297,6 +384,8 @@ async def update_cap_crowdloan_extrinsic( wallet: "Wallet", crowdloan_id: int, new_cap: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -313,6 +402,9 @@ async def update_cap_crowdloan_extrinsic( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_cap: The new fundraising cap (in TAO or Balance). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -340,14 +432,26 @@ async def update_cap_crowdloan_extrinsic( crowdloan_id=crowdloan_id, new_cap=new_cap.rao ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -358,6 +462,8 @@ async def update_end_crowdloan_extrinsic( wallet: "Wallet", crowdloan_id: int, new_end: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -374,6 +480,9 @@ async def update_end_crowdloan_extrinsic( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_end: The new block number at which the crowdloan will end. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -400,14 +509,26 @@ async def update_end_crowdloan_extrinsic( crowdloan_id=crowdloan_id, new_end=new_end ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -418,6 +539,8 @@ async def update_min_contribution_crowdloan_extrinsic( wallet: "Wallet", crowdloan_id: int, new_min_contribution: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -434,6 +557,9 @@ async def update_min_contribution_crowdloan_extrinsic( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_min_contribution: The new minimum contribution amount (in TAO or Balance). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -461,14 +587,26 @@ async def update_min_contribution_crowdloan_extrinsic( crowdloan_id=crowdloan_id, new_min_contribution=new_min_contribution.rao ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -478,6 +616,8 @@ async def withdraw_crowdloan_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -490,6 +630,9 @@ async def withdraw_crowdloan_extrinsic( subtensor: Active Subtensor connection. wallet: Wallet instance used to sign the transaction (must be unlocked). crowdloan_id: The unique identifier of the crowdloan to withdraw from. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -512,14 +655,26 @@ async def withdraw_crowdloan_extrinsic( call = await Crowdloan(subtensor).withdraw(crowdloan_id=crowdloan_id) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/asyncex/liquidity.py b/bittensor/core/extrinsics/asyncex/liquidity.py index b9b6b4e4e9..fdae17a441 100644 --- a/bittensor/core/extrinsics/asyncex/liquidity.py +++ b/bittensor/core/extrinsics/asyncex/liquidity.py @@ -1,6 +1,8 @@ from typing import Optional, TYPE_CHECKING +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Swap +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance from bittensor.utils.liquidity import price_to_tick @@ -18,6 +20,8 @@ async def add_liquidity_extrinsic( price_low: Balance, price_high: Balance, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -34,6 +38,9 @@ async def add_liquidity_extrinsic( price_low: The lower bound of the price tick range. price_high: The upper bound of the price tick range. hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -64,14 +71,26 @@ async def add_liquidity_extrinsic( hotkey=hotkey_ss58 or wallet.hotkey.ss58_address, ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -83,6 +102,8 @@ async def modify_liquidity_extrinsic( position_id: int, liquidity_delta: Balance, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -97,6 +118,9 @@ async def modify_liquidity_extrinsic( position_id: The id of the position record in the pool. liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative). hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -126,14 +150,26 @@ async def modify_liquidity_extrinsic( liquidity_delta=liquidity_delta.rao, ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -144,6 +180,8 @@ async def remove_liquidity_extrinsic( netuid: int, position_id: int, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -157,6 +195,9 @@ async def remove_liquidity_extrinsic( netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -185,14 +226,26 @@ async def remove_liquidity_extrinsic( position_id=position_id, ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -202,6 +255,8 @@ async def toggle_user_liquidity_extrinsic( wallet: "Wallet", netuid: int, enable: bool, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -214,6 +269,9 @@ async def toggle_user_liquidity_extrinsic( wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. enable: Boolean indicating whether to enable user liquidity. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -235,13 +293,25 @@ async def toggle_user_liquidity_extrinsic( enable=enable, ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index 60826ae82c..fcd681798c 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -1,7 +1,9 @@ import asyncio from typing import Optional, TYPE_CHECKING +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -48,6 +50,8 @@ async def move_stake_extrinsic( destination_hotkey_ss58: str, amount: Optional[Balance] = None, move_all_stake: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -65,6 +69,9 @@ async def move_stake_extrinsic( destination_hotkey_ss58: The SS58 address of the destination hotkey. amount: Amount to move. move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -121,14 +128,26 @@ async def move_stake_extrinsic( alpha_amount=amount.rao, ) block_hash_before = await subtensor.get_block_hash() - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = await subtensor.sim_swap( @@ -185,6 +204,8 @@ async def transfer_stake_extrinsic( origin_netuid: int, destination_netuid: int, amount: Balance, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -201,6 +222,9 @@ async def transfer_stake_extrinsic( origin_netuid: Network UID of the origin subnet. destination_netuid: Network UID of the destination subnet. amount: The amount of stake to transfer as a `Balance` object. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -251,14 +275,26 @@ async def transfer_stake_extrinsic( alpha_amount=amount.rao, ) block_hash_before = await subtensor.get_block_hash() - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = await subtensor.sim_swap( @@ -309,6 +345,8 @@ async def swap_stake_extrinsic( safe_swapping: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -327,6 +365,9 @@ async def swap_stake_extrinsic( safe_swapping: If true, enables price safety checks to protect against price impact. allow_partial_stake: If true, allows partial stake swaps when the full amount would exceed the price tolerance. rate_tolerance: Maximum allowed increase in a price ratio (0.005 = 0.5%). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -402,14 +443,26 @@ async def swap_stake_extrinsic( ) block_hash_before = await subtensor.get_block_hash() - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = await subtensor.sim_swap( diff --git a/bittensor/core/extrinsics/asyncex/proxy.py b/bittensor/core/extrinsics/asyncex/proxy.py index acf764d0b8..749934b62d 100644 --- a/bittensor/core/extrinsics/asyncex/proxy.py +++ b/bittensor/core/extrinsics/asyncex/proxy.py @@ -2,8 +2,10 @@ from typing import TYPE_CHECKING, Optional, Union from bittensor.core.chain_data.proxy import ProxyType +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Proxy from bittensor.core.extrinsics.utils import apply_pure_proxy_data +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.btlogging import logging @@ -20,6 +22,8 @@ async def add_proxy_extrinsic( delegate_ss58: str, proxy_type: Union[str, ProxyType], delay: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -35,6 +39,9 @@ async def add_proxy_extrinsic( proxy_type: The type of proxy permissions (e.g., "Any", "NonTransfer", "Governance", "Staking"). Can be a string or ProxyType enum value. delay: The number of blocks before the proxy can be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -72,14 +79,26 @@ async def add_proxy_extrinsic( delay=delay, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Proxy added successfully.[/green]") @@ -98,6 +117,8 @@ async def remove_proxy_extrinsic( delegate_ss58: str, proxy_type: Union[str, ProxyType], delay: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -112,6 +133,9 @@ async def remove_proxy_extrinsic( delegate_ss58: The SS58 address of the delegate proxy account to remove. proxy_type: The type of proxy permissions to remove. Can be a string or ProxyType enum value. delay: The number of blocks before the proxy removal takes effect. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -142,14 +166,26 @@ async def remove_proxy_extrinsic( delay=delay, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Proxy removed successfully.[/green]") @@ -165,6 +201,8 @@ async def remove_proxy_extrinsic( async def remove_proxies_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -179,6 +217,9 @@ async def remove_proxies_extrinsic( Parameters: subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object (the account whose proxies will be removed). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. @@ -200,14 +241,26 @@ async def remove_proxies_extrinsic( call = await Proxy(subtensor).remove_proxies() - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]All proxies removed successfully.[/green]") @@ -226,6 +279,8 @@ async def create_pure_proxy_extrinsic( proxy_type: Union[str, ProxyType], delay: int, index: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -240,6 +295,9 @@ async def create_pure_proxy_extrinsic( proxy_type: The type of proxy permissions for the pure proxy. Can be a string or ProxyType enum value. delay: The number of blocks before the pure proxy can be used. index: The index to use for generating the pure proxy account address. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -270,14 +328,26 @@ async def create_pure_proxy_extrinsic( index=index, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Pure proxy created successfully.[/green]") @@ -325,6 +395,8 @@ async def kill_pure_proxy_extrinsic( height: int, ext_index: int, force_proxy_type: Optional[Union[str, ProxyType]] = ProxyType.Any, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -357,6 +429,9 @@ async def kill_pure_proxy_extrinsic( the pure proxy account should be used. The spawner must have a proxy relationship of this type (or `Any`) with the pure proxy account. Defaults to `ProxyType.Any` for maximum compatibility. If `None`, Substrate will automatically select an available proxy type from the spawner's proxy relationships. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -439,6 +514,7 @@ async def kill_pure_proxy_extrinsic( real_account_ss58=pure_proxy_ss58, force_proxy_type=force_proxy_type, call=kill_pure_call, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -462,6 +538,8 @@ async def proxy_extrinsic( real_account_ss58: str, force_proxy_type: Optional[Union[str, ProxyType]], call: "GenericCall", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -477,6 +555,9 @@ async def proxy_extrinsic( force_proxy_type: The type of proxy to use for the call. If None, any proxy type can be used. Otherwise, must match one of the allowed proxy types. Can be a string or ProxyType enum value. call: The inner call to be executed on behalf of the real account. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -511,14 +592,26 @@ async def proxy_extrinsic( call=call, ) - response = await subtensor.sign_and_send_extrinsic( - call=proxy_call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=proxy_call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=proxy_call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Proxy call executed successfully.[/green]") @@ -538,6 +631,8 @@ async def proxy_announced_extrinsic( real_account_ss58: str, force_proxy_type: Optional[Union[str, ProxyType]], call: "GenericCall", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -557,6 +652,9 @@ async def proxy_announced_extrinsic( force_proxy_type: The type of proxy to use for the call. If None, any proxy type can be used. Otherwise, must match one of the allowed proxy types. Can be a string or ProxyType enum value. call: The inner call to be executed on behalf of the real account (must match the announced call_hash). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -592,14 +690,26 @@ async def proxy_announced_extrinsic( call=call, ) - response = await subtensor.sign_and_send_extrinsic( - call=proxy_call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=proxy_call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=proxy_call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Announced proxy call executed successfully.[/green]") @@ -617,6 +727,8 @@ async def announce_extrinsic( wallet: "Wallet", real_account_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -630,6 +742,9 @@ async def announce_extrinsic( wallet: Bittensor wallet object (should be the proxy account wallet). real_account_ss58: The SS58 address of the real account on whose behalf the call will be made. call_hash: The hash of the call that will be executed in the future. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -660,14 +775,26 @@ async def announce_extrinsic( call_hash=call_hash, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Proxy call announced successfully.[/green]") @@ -685,6 +812,8 @@ async def reject_announcement_extrinsic( wallet: "Wallet", delegate_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -701,6 +830,9 @@ async def reject_announcement_extrinsic( wallet: Bittensor wallet object (should be the real account wallet). delegate_ss58: The SS58 address of the delegate proxy account whose announcement is being rejected. call_hash: The hash of the call that was announced and is now being rejected. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -731,14 +863,26 @@ async def reject_announcement_extrinsic( call_hash=call_hash, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Announcement rejected successfully.[/green]") @@ -756,6 +900,8 @@ async def remove_announcement_extrinsic( wallet: "Wallet", real_account_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -772,6 +918,9 @@ async def remove_announcement_extrinsic( wallet: Bittensor wallet object (should be the proxy account wallet that made the announcement). real_account_ss58: The SS58 address of the real account on whose behalf the call was announced. call_hash: The hash of the call that was announced and is now being removed. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -802,14 +951,26 @@ async def remove_announcement_extrinsic( call_hash=call_hash, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Announcement removed successfully.[/green]") @@ -825,6 +986,8 @@ async def remove_announcement_extrinsic( async def poke_deposit_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -840,6 +1003,9 @@ async def poke_deposit_extrinsic( Parameters: subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object (the account whose deposits will be adjusted). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. @@ -866,14 +1032,26 @@ async def poke_deposit_extrinsic( call = await Proxy(subtensor).poke_deposit() - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: logging.debug("[green]Deposit poked successfully.[/green]") diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 152becc436..8a16b30c7f 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -6,7 +6,9 @@ from typing import Optional, Union, TYPE_CHECKING from bittensor.core.errors import RegistrationError +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.btlogging import logging from bittensor.utils.registration import create_pow_async, log_no_torch_error, torch @@ -20,6 +22,8 @@ async def burned_register_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", netuid: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -31,6 +35,9 @@ async def burned_register_extrinsic( subtensor: Subtensor instance. wallet: Bittensor wallet object. netuid: The ``netuid`` of the subnet to register on. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -84,14 +91,26 @@ async def burned_register_extrinsic( netuid=netuid, hotkey=wallet.hotkey.ss58_address ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) extrinsic_fee = response.extrinsic_fee logging.debug( f"The registration fee for SN #[blue]{netuid}[/blue] is [blue]{extrinsic_fee}[/blue]." @@ -140,6 +159,8 @@ async def burned_register_extrinsic( async def register_subnet_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -151,6 +172,9 @@ async def register_subnet_extrinsic( Parameters: subtensor: The subtensor interface to send the extrinsic. wallet: The wallet to be used for subnet registration. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -182,14 +206,26 @@ async def register_subnet_extrinsic( hotkey=wallet.hotkey.ss58_address ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) if not wait_for_finalization and not wait_for_inclusion: return response @@ -217,6 +253,8 @@ async def register_extrinsic( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -239,6 +277,9 @@ async def register_extrinsic( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -343,14 +384,26 @@ async def register_extrinsic( nonce=pow_result.nonce, work=[int(byte_) for byte_ in pow_result.seal], ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) if not response.success: # Look error here @@ -404,6 +457,8 @@ async def set_subnet_identity_extrinsic( discord: str, description: str, additional: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -424,6 +479,9 @@ async def set_subnet_identity_extrinsic( discord: Discord server or contact for the subnet. description: A textual description of the subnet. additional: Any additional metadata or information related to the subnet. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -454,14 +512,26 @@ async def set_subnet_identity_extrinsic( additional=additional, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) if not wait_for_finalization and not wait_for_inclusion: return response diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index 009a01d04e..a1fbb6cc08 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -2,7 +2,9 @@ from typing import Optional, TYPE_CHECKING, Literal from bittensor.core.chain_data import RootClaimType +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils import u16_normalized_float from bittensor.utils.balance import Balance @@ -41,6 +43,8 @@ async def _get_limits(subtensor: "AsyncSubtensor") -> tuple[int, float]: async def root_register_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -52,6 +56,9 @@ async def root_register_extrinsic( Parameters: subtensor: Subtensor instance to interact with the blockchain. wallet: Bittensor Wallet instance. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -109,14 +116,26 @@ async def root_register_extrinsic( hotkey=wallet.hotkey.ss58_address ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if not response.success: logging.error(f"[red]{response.message}[/red]") @@ -149,6 +168,8 @@ async def set_root_claim_type_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", new_root_claim_type: "Literal['Swap', 'Keep'] | RootClaimType | dict", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -164,6 +185,9 @@ async def set_root_claim_type_extrinsic( - RootClaimType: RootClaimType.Swap, RootClaimType.Keep - Dict: {"KeepSubnets": {"subnets": [1, 2, 3]}} - Callable: RootClaimType.KeepSubnets([1, 2, 3]) + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -186,14 +210,26 @@ async def set_root_claim_type_extrinsic( new_root_claim_type=normalized_type ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -203,6 +239,8 @@ async def claim_root_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", netuids: "UIDs", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -214,6 +252,9 @@ async def claim_root_extrinsic( subtensor: Subtensor instance to interact with the blockchain. wallet: Bittensor Wallet instance. netuids: The netuids to claim root emissions for. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -232,14 +273,26 @@ async def claim_root_extrinsic( call = await SubtensorModule(subtensor).claim_root(subnets=netuids) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index 5a3d3bdbe7..c33b461971 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -2,8 +2,9 @@ from typing import Optional, Union, TYPE_CHECKING from bittensor.core.errors import MetadataError +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Commitments, SubtensorModule -from bittensor.core.settings import version_as_int +from bittensor.core.settings import DEFAULT_MEV_PROTECTION, version_as_int from bittensor.core.types import AxonServeCallParams, ExtrinsicResponse from bittensor.utils import ( networking as net, @@ -27,6 +28,8 @@ async def serve_extrinsic( placeholder1: int = 0, placeholder2: int = 0, certificate: Optional[Certificate] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -45,6 +48,9 @@ async def serve_extrinsic( placeholder1: A placeholder for future use. placeholder2: A placeholder for future use. certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -99,15 +105,28 @@ async def serve_extrinsic( ) call = await call_function(**params.as_dict()) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - sign_with="hotkey", - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + signer_keypair=wallet.hotkey, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", + period=period, + raise_error=raise_error, + ) if response.success: logging.debug( @@ -128,6 +147,8 @@ async def serve_axon_extrinsic( netuid: int, axon: "Axon", certificate: Optional[Certificate] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -141,6 +162,9 @@ async def serve_axon_extrinsic( netuid: The ``netuid`` being served on. axon: Axon to serve. certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -181,6 +205,7 @@ async def serve_axon_extrinsic( protocol=4, netuid=netuid, certificate=certificate, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -203,8 +228,10 @@ async def publish_metadata_extrinsic( netuid: int, data_type: str, data: Union[bytes, dict], - period: Optional[int] = None, reset_bonds: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, + period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, @@ -222,6 +249,9 @@ async def publish_metadata_extrinsic( data: The actual metadata content to be published. This should be formatted or hashed according to the ``type`` specified. (Note: max ``str`` length is 128 bytes for ``'Raw0-128'``.) reset_bonds: If `True`, the function will reset the bonds for the neuron. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -253,15 +283,28 @@ async def publish_metadata_extrinsic( call = await Commitments(subtensor).set_commitment(netuid=netuid, info=info) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - sign_with=signing_keypair, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + signer_keypair=wallet.hotkey, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + sign_with=signing_keypair, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) if response.success: return response diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index f6f866a09d..8a4640dc2f 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -218,6 +218,8 @@ async def add_stake_multiple_extrinsic( netuids: UIDs, hotkey_ss58s: list[str], amounts: list[Balance], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -233,6 +235,9 @@ async def add_stake_multiple_extrinsic( netuids: List of netuids to stake to. hotkey_ss58s: List of hotkeys to stake to. amounts: List of corresponding TAO amounts to bet for each netuid and hotkey. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -355,6 +360,7 @@ async def add_stake_multiple_extrinsic( netuid=netuid, hotkey_ss58=hotkey_ss58, amount=amount, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -439,6 +445,8 @@ async def set_auto_stake_extrinsic( wallet: "Wallet", netuid: int, hotkey_ss58: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -452,6 +460,9 @@ async def set_auto_stake_extrinsic( netuid: The subnet unique identifier. hotkey_ss58: The SS58 address of the validator's hotkey to which the miner automatically stakes all rewards received from the specified subnet immediately upon receipt. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -472,14 +483,26 @@ async def set_auto_stake_extrinsic( netuid=netuid, hotkey=hotkey_ss58 ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) if response.success: logging.debug(response.message) diff --git a/bittensor/core/extrinsics/asyncex/start_call.py b/bittensor/core/extrinsics/asyncex/start_call.py index 05d9f542ab..7b27bacd11 100644 --- a/bittensor/core/extrinsics/asyncex/start_call.py +++ b/bittensor/core/extrinsics/asyncex/start_call.py @@ -1,6 +1,8 @@ from typing import TYPE_CHECKING, Optional +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse if TYPE_CHECKING: @@ -12,6 +14,8 @@ async def start_call_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", netuid: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -25,6 +29,9 @@ async def start_call_extrinsic( subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -43,14 +50,26 @@ async def start_call_extrinsic( call = await SubtensorModule(subtensor).start_call(netuid=netuid) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/asyncex/take.py b/bittensor/core/extrinsics/asyncex/take.py index b3b468ba69..56d6a42dfc 100644 --- a/bittensor/core/extrinsics/asyncex/take.py +++ b/bittensor/core/extrinsics/asyncex/take.py @@ -2,7 +2,9 @@ from bittensor_wallet.bittensor_wallet import Wallet +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse if TYPE_CHECKING: @@ -15,6 +17,8 @@ async def set_take_extrinsic( hotkey_ss58: str, take: int, action: Literal["increase_take", "decrease_take"], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -28,6 +32,9 @@ async def set_take_extrinsic( hotkey_ss58: SS58 address of the hotkey to set take for. take: The percentage of rewards that the delegate claims from nominators. action: The call function to use to set the take. Can be either "increase_take" or "decrease_take". + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -55,14 +62,26 @@ async def set_take_extrinsic( else: raise ValueError(f"Invalid action: {action}") - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) diff --git a/bittensor/core/extrinsics/asyncex/transfer.py b/bittensor/core/extrinsics/asyncex/transfer.py index cf833aa7e6..83365ea0f4 100644 --- a/bittensor/core/extrinsics/asyncex/transfer.py +++ b/bittensor/core/extrinsics/asyncex/transfer.py @@ -1,9 +1,14 @@ import asyncio from typing import TYPE_CHECKING, Optional +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import Balances from bittensor.core.extrinsics.utils import get_transfer_fn_params -from bittensor.core.settings import NETWORK_EXPLORER_MAP, DEFAULT_NETWORK +from bittensor.core.settings import ( + DEFAULT_MEV_PROTECTION, + NETWORK_EXPLORER_MAP, + DEFAULT_NETWORK, +) from bittensor.core.types import ExtrinsicResponse from bittensor.utils import ( get_explorer_url_for_network, @@ -24,6 +29,8 @@ async def transfer_extrinsic( amount: Optional[Balance], keep_alive: bool = True, transfer_all: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -38,6 +45,9 @@ async def transfer_extrinsic( amount: Amount to stake as Bittensor balance. `None` if transferring all. transfer_all: Whether to transfer all funds from this wallet to the destination address. keep_alive: If set, keeps the account alive by keeping the balance above the existential deposit. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -106,14 +116,26 @@ async def transfer_extrinsic( call = await getattr(Balances(subtensor), call_function)(**call_params) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + ) response.transaction_tao_fee = fee if response.success: diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 29028fdc7f..20abc7583a 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -4,8 +4,10 @@ from async_substrate_interface.errors import SubstrateRequestException from bittensor.core.errors import BalanceTypeError +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule from bittensor.core.extrinsics.utils import get_old_stakes +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.core.types import UIDs from bittensor.utils import format_error_message @@ -26,6 +28,8 @@ async def unstake_extrinsic( allow_partial_stake: bool = False, rate_tolerance: float = 0.005, safe_unstaking: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -43,6 +47,9 @@ async def unstake_extrinsic( allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded. safe_unstaking: If true, enables price safety checks. rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -121,16 +128,28 @@ async def unstake_extrinsic( logging.debug(logging_message) block_hash_before = await subtensor.get_block_hash() - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - nonce_key="coldkeypub", - use_nonce=True, - period=period, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + nonce_key="coldkeypub", + use_nonce=True, + period=period, + raise_error=raise_error, + ) if response.success: sim_swap = await subtensor.sim_swap( @@ -189,6 +208,8 @@ async def unstake_all_extrinsic( netuid: int, hotkey_ss58: str, rate_tolerance: Optional[float] = 0.005, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -203,6 +224,9 @@ async def unstake_all_extrinsic( hotkey_ss58: The SS58 address of the hotkey to unstake from. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. If not passed (None), then unstaking goes without price limit. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -228,16 +252,28 @@ async def unstake_all_extrinsic( limit_price=limit_price, ) - return await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - nonce_key="coldkeypub", - use_nonce=True, - period=period, - raise_error=raise_error, - ) + if mev_protection: + return await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + return await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + nonce_key="coldkeypub", + use_nonce=True, + period=period, + raise_error=raise_error, + ) except Exception as error: return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error) @@ -251,6 +287,8 @@ async def unstake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, rate_tolerance: Optional[float] = 0.05, unstake_all: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -267,6 +305,9 @@ async def unstake_multiple_extrinsic( amounts: List of amounts to unstake. If ``None``, unstake all. rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%). unstake_all: If true, unstakes all tokens. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -399,6 +440,7 @@ async def unstake_multiple_extrinsic( hotkey_ss58=hotkey_ss58, netuid=netuid, rate_tolerance=rate_tolerance, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -411,6 +453,8 @@ async def unstake_multiple_extrinsic( netuid=netuid, hotkey_ss58=hotkey_ss58, amount=unstaking_balance, + rate_tolerance=rate_tolerance, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 37370642e4..eba2259ffd 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -4,8 +4,9 @@ from bittensor_drand import get_encrypted_commit +from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.pallets import SubtensorModule -from bittensor.core.settings import version_as_int +from bittensor.core.settings import DEFAULT_MEV_PROTECTION, version_as_int from bittensor.core.types import ExtrinsicResponse, Salt, UIDs, Weights from bittensor.utils import get_mechid_storage_index from bittensor.utils.btlogging import logging @@ -29,6 +30,8 @@ async def commit_timelocked_weights_extrinsic( block_time: Union[int, float], commit_reveal_version: int = 4, version_key: int = version_as_int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -46,6 +49,9 @@ async def commit_timelocked_weights_extrinsic( block_time: The number of seconds for block duration. commit_reveal_version: The version of the commit-reveal in the chain. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -98,17 +104,30 @@ async def commit_timelocked_weights_extrinsic( commit_reveal_version=commit_reveal_version, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - use_nonce=True, - period=period, - sign_with=signing_keypair, - nonce_key=signing_keypair, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + signer_keypair=wallet.hotkey, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, + sign_with=signing_keypair, + nonce_key=signing_keypair, + raise_error=raise_error, + ) if response.success: logging.debug(response.message) @@ -134,6 +153,8 @@ async def commit_weights_extrinsic( weights: Weights, salt: Salt, version_key: int = version_as_int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -150,6 +171,9 @@ async def commit_weights_extrinsic( weights: NumPy array of weight values corresponding to each UID. salt: list of randomly generated integers as salt to generated weighted hash. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -186,17 +210,30 @@ async def commit_weights_extrinsic( commit_hash=commit_hash, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - use_nonce=True, - period=period, - sign_with=signing_keypair, - nonce_key=signing_keypair, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + signer_keypair=wallet.hotkey, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, + sign_with=signing_keypair, + nonce_key=signing_keypair, + raise_error=raise_error, + ) if response.success: logging.debug(response.message) @@ -218,6 +255,8 @@ async def reveal_weights_extrinsic( weights: Weights, salt: Salt, version_key: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -235,6 +274,9 @@ async def reveal_weights_extrinsic( weights: List of weight values corresponding to each UID. salt: List of salt values corresponding to the hash function. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -266,17 +308,30 @@ async def reveal_weights_extrinsic( version_key=version_key, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - use_nonce=True, - period=period, - sign_with=signing_keypair, - nonce_key=signing_keypair, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + signer_keypair=wallet.hotkey, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, + sign_with=signing_keypair, + nonce_key=signing_keypair, + raise_error=raise_error, + ) if response.success: logging.debug(response.message) @@ -297,6 +352,8 @@ async def set_weights_extrinsic( uids: UIDs, weights: Weights, version_key: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -313,6 +370,9 @@ async def set_weights_extrinsic( uids: List of neuron UIDs for which weights are being revealed. weights: List of weight values corresponding to each UID. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -343,17 +403,30 @@ async def set_weights_extrinsic( version_key=version_key, ) - response = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - use_nonce=True, - nonce_key=signing_keypair, - sign_with=signing_keypair, - raise_error=raise_error, - ) + if mev_protection: + response = await submit_encrypted_extrinsic( + subtensor=subtensor, + wallet=wallet, + call=call, + signer_keypair=wallet.hotkey, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, + ) + else: + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + use_nonce=True, + nonce_key=signing_keypair, + sign_with=signing_keypair, + raise_error=raise_error, + ) if response.success: logging.debug(response.message) From 1bc7ce1f1d5e90fba4d9e1d75970f73388710df6 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 19:33:04 -0800 Subject: [PATCH 43/67] update all async subtensor methods --- bittensor/core/async_subtensor.py | 300 +++++++++++++++++++++++++++++- 1 file changed, 298 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index f6c9fc29a5..2a01b78a72 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5876,6 +5876,8 @@ async def add_liquidity( price_low: Balance, price_high: Balance, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5891,6 +5893,9 @@ async def add_liquidity( price_low: The lower bound of the price tick range. In TAO. price_high: The upper bound of the price tick range. In TAO. hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5912,6 +5917,7 @@ async def add_liquidity( price_low=price_low, price_high=price_high, hotkey_ss58=hotkey_ss58, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5924,6 +5930,8 @@ async def add_stake_multiple( netuids: UIDs, hotkey_ss58s: list[str], amounts: list[Balance], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5938,6 +5946,9 @@ async def add_stake_multiple( netuids: List of subnet UIDs. hotkey_ss58s: List of ``SS58`` addresses of hotkeys to stake to. amounts: List of corresponding TAO amounts to bet for each netuid and hotkey. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -5957,6 +5968,7 @@ async def add_stake_multiple( netuids=netuids, hotkey_ss58s=hotkey_ss58s, amounts=amounts, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -5969,6 +5981,8 @@ async def add_proxy( delegate_ss58: str, proxy_type: Union[str, "ProxyType"], delay: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5987,6 +6001,9 @@ async def add_proxy( proxy_type: The type of proxy permissions (e.g., "Any", "NonTransfer", "Governance", "Staking"). Can be a string or ProxyType enum value. delay: The number of blocks before the proxy can be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6007,6 +6024,7 @@ async def add_proxy( delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6018,6 +6036,8 @@ async def announce_proxy( wallet: "Wallet", real_account_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6034,6 +6054,9 @@ async def announce_proxy( wallet: Bittensor wallet object (should be the proxy account wallet). real_account_ss58: The SS58 address of the real account on whose behalf the call will be made. call_hash: The hash of the call that will be executed in the future. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6053,6 +6076,7 @@ async def announce_proxy( wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6063,6 +6087,8 @@ async def burned_register( self, wallet: "Wallet", netuid: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6075,6 +6101,9 @@ async def burned_register( Parameters: wallet: The wallet associated with the neuron to be registered. netuid: The unique identifier of the subnet. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6090,6 +6119,7 @@ async def burned_register( return await root_register_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6100,6 +6130,7 @@ async def burned_register( subtensor=self, wallet=wallet, netuid=netuid, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6110,6 +6141,8 @@ async def claim_root( self, wallet: "Wallet", netuids: "UIDs", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6120,6 +6153,9 @@ async def claim_root( Parameters: wallet: Bittensor Wallet instance. netuids: The netuids to claim root emissions for. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6134,6 +6170,7 @@ async def claim_root( subtensor=self, wallet=wallet, netuids=netuids, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6150,6 +6187,8 @@ async def commit_weights( mechid: int = 0, version_key: int = version_as_int, max_attempts: int = 5, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = 16, raise_error: bool = True, wait_for_inclusion: bool = False, @@ -6168,6 +6207,9 @@ async def commit_weights( mechid: The subnet mechanism unique identifier. version_key: Version key for compatibility with the network. max_attempts: The number of maximum attempts to commit weights. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6206,6 +6248,7 @@ async def commit_weights( uids=uids, weights=weights, salt=salt, + mev_protection=mev_protection, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, @@ -6229,6 +6272,8 @@ async def contribute_crowdloan( wallet: "Wallet", crowdloan_id: int, amount: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6241,6 +6286,9 @@ async def contribute_crowdloan( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to contribute to. amount: Amount to contribute. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6256,6 +6304,7 @@ async def contribute_crowdloan( wallet=wallet, crowdloan_id=crowdloan_id, amount=amount, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6271,6 +6320,8 @@ async def create_crowdloan( end: int, call: Optional["GenericCall"] = None, target_address: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6287,6 +6338,9 @@ async def create_crowdloan( end: Block number when the campaign ends. call: Runtime call data (e.g., subtensor::register_leased_network). target_address: SS58 address to transfer funds to on success. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6306,6 +6360,7 @@ async def create_crowdloan( end=end, call=call, target_address=target_address, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6318,6 +6373,8 @@ async def create_pure_proxy( proxy_type: Union[str, "ProxyType"], delay: int, index: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6335,6 +6392,9 @@ async def create_pure_proxy( proxy_type: The type of proxy permissions for the pure proxy. Can be a string or ProxyType enum value. delay: The number of blocks before the pure proxy can be used. index: The index to use for generating the pure proxy account address. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6356,6 +6416,7 @@ async def create_pure_proxy( proxy_type=proxy_type, delay=delay, index=index, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6366,6 +6427,8 @@ async def dissolve_crowdloan( self, wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6380,6 +6443,9 @@ async def dissolve_crowdloan( Parameters: wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to dissolve. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6400,6 +6466,7 @@ async def dissolve_crowdloan( subtensor=self, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6410,6 +6477,8 @@ async def finalize_crowdloan( self, wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6423,6 +6492,9 @@ async def finalize_crowdloan( Parameters: wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to finalize. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. @@ -6435,6 +6507,7 @@ async def finalize_crowdloan( subtensor=self, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6451,6 +6524,8 @@ async def kill_pure_proxy( height: int, ext_index: int, force_proxy_type: Optional[Union[str, "ProxyType"]] = ProxyType.Any, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6483,6 +6558,9 @@ async def kill_pure_proxy( type (or `Any`) with the pure proxy account. Defaults to `ProxyType.Any` for maximum compatibility. If `None`, Substrate will automatically select an available proxy type from the spawner's proxy relationships. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6512,6 +6590,7 @@ async def kill_pure_proxy( height=height, ext_index=ext_index, force_proxy_type=force_proxy_type, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6586,6 +6665,8 @@ async def modify_liquidity( position_id: int, liquidity_delta: Balance, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6599,6 +6680,9 @@ async def modify_liquidity( position_id: The id of the position record in the pool. liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative). hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6645,6 +6729,7 @@ async def modify_liquidity( position_id=position_id, liquidity_delta=liquidity_delta, hotkey_ss58=hotkey_ss58, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6660,6 +6745,8 @@ async def move_stake( destination_hotkey_ss58: str, amount: Optional[Balance] = None, move_all_stake: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6676,6 +6763,9 @@ async def move_stake( destination_hotkey_ss58: The SS58 address of the destination hotkey. amount: Amount of stake to move. move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6696,6 +6786,7 @@ async def move_stake( destination_hotkey_ss58=destination_hotkey_ss58, amount=amount, move_all_stake=move_all_stake, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6705,6 +6796,8 @@ async def move_stake( async def poke_deposit( self, wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6719,6 +6812,9 @@ async def poke_deposit( Parameters: wallet: Bittensor wallet object (the account whose deposits will be adjusted). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. @@ -6739,6 +6835,7 @@ async def poke_deposit( return await poke_deposit_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6751,6 +6848,8 @@ async def proxy( real_account_ss58: str, force_proxy_type: Optional[Union[str, "ProxyType"]], call: "GenericCall", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6769,6 +6868,9 @@ async def proxy( force_proxy_type: The type of proxy to use for the call. If None, any proxy type can be used. Otherwise, must match one of the allowed proxy types. Can be a string or ProxyType enum value. call: The inner call to be executed on behalf of the real account. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6789,6 +6891,7 @@ async def proxy( real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6802,6 +6905,8 @@ async def proxy_announced( real_account_ss58: str, force_proxy_type: Optional[Union[str, "ProxyType"]], call: "GenericCall", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6821,6 +6926,9 @@ async def proxy_announced( force_proxy_type: The type of proxy to use for the call. If None, any proxy type can be used. Otherwise, must match one of the allowed proxy types. Can be a string or ProxyType enum value. call: The inner call to be executed on behalf of the real account (must match the announced call_hash). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6842,6 +6950,7 @@ async def proxy_announced( real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6852,6 +6961,8 @@ async def refund_crowdloan( self, wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6866,6 +6977,9 @@ async def refund_crowdloan( Parameters: wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to refund. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6886,6 +7000,7 @@ async def refund_crowdloan( subtensor=self, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6897,6 +7012,8 @@ async def reject_proxy_announcement( wallet: "Wallet", delegate_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6913,6 +7030,9 @@ async def reject_proxy_announcement( wallet: Bittensor wallet object (should be the real account wallet). delegate_ss58: The SS58 address of the delegate proxy account whose announcement is being rejected. call_hash: The hash of the call that was announced and is now being rejected. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6931,6 +7051,7 @@ async def reject_proxy_announcement( wallet=wallet, delegate_ss58=delegate_ss58, call_hash=call_hash, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -6949,6 +7070,8 @@ async def register( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6972,6 +7095,9 @@ async def register( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If ``true``, the registration process will log more information. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -6997,6 +7123,7 @@ async def register( dev_id=dev_id, output_in_place=output_in_place, log_verbose=log_verbose, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7006,6 +7133,8 @@ async def register( async def register_subnet( self: "AsyncSubtensor", wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7016,6 +7145,9 @@ async def register_subnet( Parameters: wallet: The wallet to be used for subnet registration. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7029,6 +7161,7 @@ async def register_subnet( return await register_subnet_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7040,6 +7173,8 @@ async def remove_proxy_announcement( wallet: "Wallet", real_account_ss58: str, call_hash: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7056,6 +7191,9 @@ async def remove_proxy_announcement( wallet: Bittensor wallet object (should be the proxy account wallet that made the announcement). real_account_ss58: The SS58 address of the real account on whose behalf the call was announced. call_hash: The hash of the call that was announced and is now being removed. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7075,6 +7213,7 @@ async def remove_proxy_announcement( wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7087,6 +7226,8 @@ async def remove_liquidity( netuid: int, position_id: int, hotkey_ss58: Optional[str] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7099,6 +7240,9 @@ async def remove_liquidity( netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. hotkey_ss58: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7120,6 +7264,7 @@ async def remove_liquidity( netuid=netuid, position_id=position_id, hotkey_ss58=hotkey_ss58, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7129,6 +7274,8 @@ async def remove_liquidity( async def remove_proxies( self, wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7144,6 +7291,9 @@ async def remove_proxies( Parameters: wallet: Bittensor wallet object. The account whose proxies will be removed (the delegator). All proxy relationships where wallet.coldkey.ss58_address is the real account will be removed. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7161,6 +7311,7 @@ async def remove_proxies( return await remove_proxies_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7173,6 +7324,8 @@ async def remove_proxy( delegate_ss58: str, proxy_type: Union[str, "ProxyType"], delay: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7190,6 +7343,9 @@ async def remove_proxy( delegate_ss58: The SS58 address of the delegate proxy account to remove. proxy_type: The type of proxy permissions to remove. Can be a string or ProxyType enum value. delay: The number of blocks before the proxy removal takes effect. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7210,6 +7366,7 @@ async def remove_proxy( delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7226,6 +7383,8 @@ async def reveal_weights( mechid: int = 0, max_attempts: int = 5, version_key: int = version_as_int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = 16, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7244,6 +7403,9 @@ async def reveal_weights( mechid: The subnet mechanism unique identifier. max_attempts: The number of maximum attempts to reveal weights. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7276,6 +7438,7 @@ async def reveal_weights( weights=weights, salt=salt, version_key=version_key, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7294,6 +7457,8 @@ async def reveal_weights( async def root_register( self, wallet: "Wallet", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7304,6 +7469,9 @@ async def root_register( Parameters: wallet: The wallet associated with the neuron to be registered. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7318,6 +7486,7 @@ async def root_register( return await root_register_extrinsic( subtensor=self, wallet=wallet, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7328,6 +7497,8 @@ async def root_set_pending_childkey_cooldown( self, wallet: "Wallet", cooldown: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7338,6 +7509,9 @@ async def root_set_pending_childkey_cooldown( Parameters: wallet: bittensor wallet instance. cooldown: the number of blocks to setting pending childkey cooldown. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7354,6 +7528,7 @@ async def root_set_pending_childkey_cooldown( subtensor=self, wallet=wallet, cooldown=cooldown, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7365,6 +7540,8 @@ async def set_auto_stake( wallet: "Wallet", netuid: int, hotkey_ss58: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7377,6 +7554,9 @@ async def set_auto_stake( netuid: The subnet unique identifier. hotkey_ss58: The SS58 address of the validator's hotkey to which the miner automatically stakes all rewards received from the specified subnet immediately upon receipt. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7395,6 +7575,7 @@ async def set_auto_stake( wallet=wallet, netuid=netuid, hotkey_ss58=hotkey_ss58, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7407,6 +7588,8 @@ async def set_children( hotkey_ss58: str, netuid: int, children: list[tuple[float, str]], + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7420,6 +7603,9 @@ async def set_children( hotkey_ss58: The `SS58` address of the neuron's hotkey. netuid: The netuid value. children: A list of children with their proportions. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7449,6 +7635,7 @@ async def set_children( hotkey_ss58=hotkey_ss58, netuid=netuid, children=children, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7460,6 +7647,8 @@ async def set_delegate_take( wallet: "Wallet", hotkey_ss58: str, take: float, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7473,6 +7662,9 @@ async def set_delegate_take( wallet: bittensor wallet instance. hotkey_ss58: The ``SS58`` address of the neuron's hotkey. take: Percentage reward for the delegate. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7515,6 +7707,7 @@ async def set_delegate_take( hotkey_ss58=hotkey_ss58, take=take_u16, action="increase_take" if current_take_u16 < take_u16 else "decrease_take", + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_finalization=wait_for_finalization, @@ -7531,6 +7724,8 @@ async def set_root_claim_type( self, wallet: "Wallet", new_root_claim_type: "Literal['Swap', 'Keep'] | RootClaimType | dict", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7543,8 +7738,11 @@ async def set_root_claim_type( new_root_claim_type: The new root claim type to set. Can be: - String: "Swap" or "Keep" - RootClaimType: RootClaimType.Swap, RootClaimType.Keep - - Dict: {"KeepSubnets": {"subnets": [1, 2, 3]}} - - Callable: RootClaimType.KeepSubnets([1, 2, 3]) + - Dict: {"KeepSubnets": {"subnets": [1, 2, 3]}} + - Callable: RootClaimType.KeepSubnets([1, 2, 3]) + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7559,6 +7757,7 @@ async def set_root_claim_type( subtensor=self, wallet=wallet, new_root_claim_type=new_root_claim_type, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7570,6 +7769,8 @@ async def set_subnet_identity( wallet: "Wallet", netuid: int, subnet_identity: SubnetIdentity, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7583,6 +7784,9 @@ async def set_subnet_identity( netuid: The unique ID of the network on which the operation takes place. subnet_identity: The identity data of the subnet including attributes like name, GitHub repository, contact, URL, discord, description, and any additional metadata. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7605,6 +7809,7 @@ async def set_subnet_identity( discord=subnet_identity.discord, description=subnet_identity.description, additional=subnet_identity.additional, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7622,6 +7827,8 @@ async def set_weights( commit_reveal_version: int = 4, max_attempts: int = 5, version_key: int = version_as_int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = 8, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7646,6 +7853,9 @@ async def set_weights( commit_reveal_version: The version of the chain commit-reveal protocol to use. max_attempts: The number of maximum attempts to set weights. version_key: Version key for compatibility with the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7708,6 +7918,7 @@ async def _blocks_weight_limit() -> bool: block_time=block_time, commit_reveal_version=commit_reveal_version, version_key=version_key, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7739,6 +7950,7 @@ async def _blocks_weight_limit() -> bool: uids=uids, weights=weights, version_key=version_key, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7761,6 +7973,8 @@ async def serve_axon( netuid: int, axon: "Axon", certificate: Optional[Certificate] = None, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7776,6 +7990,9 @@ async def serve_axon( netuid: The unique identifier of the subnetwork. axon: The Axon instance to be registered for serving. certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7794,6 +8011,7 @@ async def serve_axon( netuid=netuid, axon=axon, certificate=certificate, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7805,6 +8023,8 @@ async def set_commitment( wallet: "Wallet", netuid: int, data: str, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7820,6 +8040,9 @@ async def set_commitment( wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7845,6 +8068,7 @@ async def set_commitment( netuid=netuid, data_type=f"Raw{len(data)}", data=data.encode(), + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7911,6 +8135,8 @@ async def start_call( self, wallet: "Wallet", netuid: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7923,6 +8149,9 @@ async def start_call( Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -7937,6 +8166,7 @@ async def start_call( subtensor=self, wallet=wallet, netuid=netuid, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -7953,6 +8183,8 @@ async def swap_stake( safe_swapping: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -7975,6 +8207,9 @@ async def swap_stake( rate_tolerance: The maximum allowed increase in the price ratio between subnets (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when safe_staking is True. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8003,6 +8238,7 @@ async def swap_stake( safe_swapping=safe_swapping, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8014,6 +8250,8 @@ async def toggle_user_liquidity( wallet: "Wallet", netuid: int, enable: bool, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8025,6 +8263,9 @@ async def toggle_user_liquidity( wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. enable: Boolean indicating whether to enable user liquidity. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8042,6 +8283,7 @@ async def toggle_user_liquidity( wallet=wallet, netuid=netuid, enable=enable, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8055,6 +8297,8 @@ async def transfer( amount: Optional[Balance], transfer_all: bool = False, keep_alive: bool = True, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8069,6 +8313,9 @@ async def transfer( amount: Number of tokens to transfer. `None` is transferring all. transfer_all: Flag to transfer all tokens. keep_alive: Flag to keep the connection alive. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8087,6 +8334,7 @@ async def transfer( amount=amount, transfer_all=transfer_all, keep_alive=keep_alive, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8101,6 +8349,8 @@ async def transfer_stake( origin_netuid: int, destination_netuid: int, amount: Balance, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8116,6 +8366,9 @@ async def transfer_stake( origin_netuid: The source subnet UID. destination_netuid: The destination subnet UID. amount: Amount to transfer. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8135,6 +8388,7 @@ async def transfer_stake( origin_netuid=origin_netuid, destination_netuid=destination_netuid, amount=amount, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8150,6 +8404,8 @@ async def unstake( allow_partial_stake: bool = False, safe_unstaking: bool = False, rate_tolerance: float = 0.005, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8171,6 +8427,9 @@ async def unstake( 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. safe_unstaking: If true, enables price safety checks to protect against fluctuating prices. The unstake will only execute if the price change doesn't exceed the rate tolerance. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8194,6 +8453,7 @@ async def unstake( allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, safe_unstaking=safe_unstaking, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8206,6 +8466,8 @@ async def unstake_all( netuid: int, hotkey_ss58: str, rate_tolerance: Optional[float] = 0.005, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8219,6 +8481,9 @@ async def unstake_all( hotkey_ss58: The SS58 address of the hotkey to unstake from. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. If not passed (None), then unstaking goes without price limit. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8274,6 +8539,7 @@ async def unstake_all( netuid=netuid, hotkey_ss58=hotkey_ss58, rate_tolerance=rate_tolerance, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8287,6 +8553,8 @@ async def unstake_multiple( hotkey_ss58s: list[str], amounts: Optional[list[Balance]] = None, unstake_all: bool = False, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8302,6 +8570,9 @@ async def unstake_multiple( hotkey_ss58s: A list of hotkey `SS58` addresses to unstake from. amounts: The amounts of TAO to unstake from each hotkey. If not provided, unstakes all. unstake_all: If true, unstakes all tokens. If `True` amounts are ignored. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8322,6 +8593,7 @@ async def unstake_multiple( hotkey_ss58s=hotkey_ss58s, amounts=amounts, unstake_all=unstake_all, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8333,6 +8605,8 @@ async def update_cap_crowdloan( wallet: "Wallet", crowdloan_id: int, new_cap: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8348,6 +8622,9 @@ async def update_cap_crowdloan( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_cap: The new fundraising cap (in TAO or Balance). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8368,6 +8645,7 @@ async def update_cap_crowdloan( wallet=wallet, crowdloan_id=crowdloan_id, new_cap=new_cap, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8379,6 +8657,8 @@ async def update_end_crowdloan( wallet: "Wallet", crowdloan_id: int, new_end: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8394,6 +8674,9 @@ async def update_end_crowdloan( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_end: The new block number at which the crowdloan will end. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8415,6 +8698,7 @@ async def update_end_crowdloan( wallet=wallet, crowdloan_id=crowdloan_id, new_end=new_end, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8426,6 +8710,8 @@ async def update_min_contribution_crowdloan( wallet: "Wallet", crowdloan_id: int, new_min_contribution: "Balance", + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8441,6 +8727,9 @@ async def update_min_contribution_crowdloan( wallet: Bittensor Wallet instance used to sign the transaction. crowdloan_id: The unique identifier of the crowdloan to update. new_min_contribution: The new minimum contribution amount (in TAO or Balance). + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8461,6 +8750,7 @@ async def update_min_contribution_crowdloan( wallet=wallet, crowdloan_id=crowdloan_id, new_min_contribution=new_min_contribution, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -8471,6 +8761,8 @@ async def withdraw_crowdloan( self, wallet: "Wallet", crowdloan_id: int, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -8482,6 +8774,9 @@ async def withdraw_crowdloan( Parameters: wallet: Wallet instance used to sign the transaction (must be unlocked). crowdloan_id: The unique identifier of the crowdloan to withdraw from. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -8500,6 +8795,7 @@ async def withdraw_crowdloan( subtensor=self, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, From 1b84ee33cba8d3f842281364f31f9710c11feb99 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 19:33:18 -0800 Subject: [PATCH 44/67] fix unit tests --- .../extrinsics/asyncex/test_proxy.py | 2 + .../extrinsics/asyncex/test_unstaking.py | 7 +- tests/unit_tests/test_async_subtensor.py | 68 +++++++++++++------ tests/unit_tests/test_stream.py | 18 +++-- tests/unit_tests/test_subtensor.py | 5 +- 5 files changed, 73 insertions(+), 27 deletions(-) diff --git a/tests/unit_tests/extrinsics/asyncex/test_proxy.py b/tests/unit_tests/extrinsics/asyncex/test_proxy.py index fd79ca3615..d5287ec407 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_proxy.py +++ b/tests/unit_tests/extrinsics/asyncex/test_proxy.py @@ -1,6 +1,7 @@ import pytest from bittensor.core.extrinsics.asyncex import proxy +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from scalecodec.types import GenericCall from bittensor_wallet import Wallet @@ -253,6 +254,7 @@ async def test_kill_pure_proxy_extrinsic(subtensor, mocker): real_account_ss58=pure_proxy_ss58, force_proxy_type=proxy.ProxyType.Any, call=mocked_pallet_call.return_value, + mev_protection=DEFAULT_MEV_PROTECTION, period=None, raise_error=False, wait_for_inclusion=True, diff --git a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py index 6d698ef8c1..9d1176a942 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py +++ b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py @@ -1,6 +1,7 @@ import pytest from bittensor.core.extrinsics.asyncex import unstaking +from bittensor.core.settings import DEFAULT_MEV_PROTECTION from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -157,9 +158,11 @@ async def test_unstake_multiple_extrinsic_some_unstake_is_happy(fake_wallet, moc mocked_unstake_extrinsic.assert_awaited_once_with( subtensor=fake_subtensor, wallet=fake_wallet, - amount=Balance.from_tao(1.1, sn_5), - hotkey_ss58=hotkey_ss58s[0], netuid=fake_netuids[0], + hotkey_ss58=hotkey_ss58s[0], + amount=Balance.from_tao(1.1, sn_5), + rate_tolerance=0.05, + mev_protection=DEFAULT_MEV_PROTECTION, period=None, raise_error=False, wait_for_inclusion=wait_for_inclusion, diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 300f8c6d84..a2c06022fd 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -18,7 +18,7 @@ ) from bittensor.core.errors import BalanceTypeError from bittensor.core.types import ExtrinsicResponse -from bittensor.core.settings import DEFAULT_PERIOD +from bittensor.core.settings import DEFAULT_MEV_PROTECTION, DEFAULT_PERIOD from bittensor.utils import U64_MAX, get_function_name from bittensor.utils.balance import Balance @@ -2592,11 +2592,12 @@ async def test_transfer_success(subtensor, fake_wallet, mocker): destination_ss58=fake_destination, amount=fake_amount, transfer_all=fake_transfer_all, - wait_for_inclusion=True, - wait_for_finalization=False, keep_alive=True, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=False, ) assert result == mocked_transfer_extrinsic.return_value @@ -2628,6 +2629,7 @@ async def test_register_success(subtensor, fake_wallet, mocker): subtensor=subtensor, tpb=256, update_interval=None, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_finalization=True, @@ -2666,10 +2668,11 @@ async def test_set_children(subtensor, fake_wallet, mocker): hotkey_ss58=fake_wallet.hotkey.ss58_address, netuid=1, children=fake_children, - wait_for_finalization=True, - wait_for_inclusion=True, - raise_error=False, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, + raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert result == mocked_set_children_extrinsic.return_value @@ -2795,10 +2798,11 @@ async def test_set_subnet_identity(mocker, subtensor, fake_wallet): discord=fake_subnet_identity.discord, description=fake_subnet_identity.description, additional=fake_subnet_identity.additional, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, - wait_for_finalization=True, wait_for_inclusion=True, + wait_for_finalization=True, ) assert result == mocked_extrinsic.return_value @@ -2902,10 +2906,11 @@ async def test_start_call(subtensor, mocker): subtensor=subtensor, wallet=wallet_name, netuid=netuid, - wait_for_inclusion=True, - wait_for_finalization=False, + mev_protection=False, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=False, ) assert result == mocked_extrinsic.return_value @@ -3397,10 +3402,11 @@ async def test_unstake_all(subtensor, fake_wallet, mocker): hotkey_ss58=fake_wallet.hotkey.ss58_address, netuid=1, rate_tolerance=0.005, - wait_for_inclusion=True, - wait_for_finalization=True, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert result == fake_unstake_all_extrinsic.return_value @@ -3623,10 +3629,11 @@ async def test_add_liquidity(subtensor, fake_wallet, mocker): price_low=Balance.from_tao(180).rao, price_high=Balance.from_tao(130).rao, hotkey_ss58=None, - wait_for_inclusion=True, - wait_for_finalization=True, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert result == mocked_extrinsic.return_value @@ -3657,10 +3664,11 @@ async def test_modify_liquidity(subtensor, fake_wallet, mocker): position_id=position_id, liquidity_delta=Balance.from_tao(150), hotkey_ss58=None, - wait_for_inclusion=True, - wait_for_finalization=True, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert result == mocked_extrinsic.return_value @@ -3689,10 +3697,11 @@ async def test_remove_liquidity(subtensor, fake_wallet, mocker): netuid=netuid, position_id=position_id, hotkey_ss58=None, - wait_for_inclusion=True, - wait_for_finalization=True, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert result == mocked_extrinsic.return_value @@ -3720,10 +3729,11 @@ async def test_toggle_user_liquidity(subtensor, fake_wallet, mocker): wallet=fake_wallet, netuid=netuid, enable=enable, - wait_for_inclusion=True, - wait_for_finalization=True, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert result == mocked_extrinsic.return_value @@ -4213,6 +4223,7 @@ async def test_set_auto_stake(subtensor, mocker): wallet=wallet, netuid=netuid, hotkey_ss58=hotkey, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4329,6 +4340,7 @@ async def test_contribute_crowdloan(mocker, subtensor): wallet=wallet, crowdloan_id=crowdloan_id, amount=amount, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4374,6 +4386,7 @@ async def test_create_crowdloan(mocker, subtensor): end=end, call=call, target_address=target_address, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4413,6 +4426,7 @@ async def test_crowdloan_methods_with_crowdloan_id_parameter( subtensor=subtensor, wallet=wallet, crowdloan_id=crowdloan_id, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4446,6 +4460,7 @@ async def test_update_cap_crowdloan(mocker, subtensor): wallet=wallet, crowdloan_id=crowdloan_id, new_cap=new_cap, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4479,6 +4494,7 @@ async def test_update_end_crowdloan(mocker, subtensor): wallet=wallet, crowdloan_id=crowdloan_id, new_end=new_end, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4512,6 +4528,7 @@ async def test_update_min_contribution_crowdloan(mocker, subtensor): wallet=wallet, crowdloan_id=crowdloan_id, new_min_contribution=new_min_contribution, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4956,6 +4973,7 @@ async def test_claim_root(mocker, subtensor): subtensor=subtensor, wallet=wallet, netuids=netuids, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -4984,6 +5002,7 @@ async def test_set_root_claim_type(mocker, subtensor): subtensor=subtensor, wallet=faked_wallet, new_root_claim_type=fake_new_root_claim_type, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5314,6 +5333,7 @@ async def test_add_proxy(mocker, subtensor): delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5346,6 +5366,7 @@ async def test_announce_proxy(mocker, subtensor): wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5381,6 +5402,7 @@ async def test_create_pure_proxy(mocker, subtensor): proxy_type=proxy_type, delay=delay, index=index, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5426,6 +5448,7 @@ async def test_kill_pure_proxy(mocker, subtensor): height=height, ext_index=ext_index, force_proxy_type=async_subtensor.ProxyType.Any, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5450,6 +5473,7 @@ async def test_poke_deposit(mocker, subtensor): mocked_poke_deposit_extrinsic.assert_awaited_once_with( subtensor=subtensor, wallet=wallet, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5483,6 +5507,7 @@ async def test_proxy(mocker, subtensor): real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5521,6 +5546,7 @@ async def test_proxy_announced(mocker, subtensor): real_account_ss58=real_account_ss58, force_proxy_type=force_proxy_type, call=call, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5553,6 +5579,7 @@ async def test_reject_proxy_announcement(mocker, subtensor): wallet=wallet, delegate_ss58=delegate_ss58, call_hash=call_hash, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5585,6 +5612,7 @@ async def test_remove_proxy_announcement(mocker, subtensor): wallet=wallet, real_account_ss58=real_account_ss58, call_hash=call_hash, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5609,6 +5637,7 @@ async def test_remove_proxies(mocker, subtensor): mocked_remove_proxies_extrinsic.assert_awaited_once_with( subtensor=subtensor, wallet=wallet, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, @@ -5644,6 +5673,7 @@ async def test_remove_proxy(mocker, subtensor): delegate_ss58=delegate_ss58, proxy_type=proxy_type, delay=delay, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, diff --git a/tests/unit_tests/test_stream.py b/tests/unit_tests/test_stream.py index 04a38cb7c0..be333d59b3 100644 --- a/tests/unit_tests/test_stream.py +++ b/tests/unit_tests/test_stream.py @@ -68,6 +68,7 @@ class TestBTStreamingResponseModel: def test_bt_streaming_response_model_creation(self): """Test BTStreamingResponseModel initialization.""" + async def mock_token_streamer(send: Send): await send({"type": "http.response.body", "body": b"test"}) @@ -76,6 +77,7 @@ async def mock_token_streamer(send: Send): def test_bt_streaming_response_model_validation(self): """Test that BTStreamingResponseModel validates token_streamer type.""" + # Should accept callable async def valid_streamer(send: Send): pass @@ -94,6 +96,7 @@ class TestBTStreamingResponse: def test_bt_streaming_response_creation(self): """Test BTStreamingResponse initialization.""" + async def mock_token_streamer(send: Send): await send({"type": "http.response.body", "body": b"test"}) @@ -106,6 +109,7 @@ async def mock_token_streamer(send: Send): def test_bt_streaming_response_without_synapse(self): """Test BTStreamingResponse initialization without synapse.""" + async def mock_token_streamer(send: Send): pass @@ -132,12 +136,14 @@ async def mock_token_streamer(send: Send): # Verify send was called with correct structure assert send_mock.call_count == 3 - + # First call: start response with headers first_call = send_mock.call_args_list[0][0][0] assert first_call["type"] == "http.response.start" assert first_call["status"] == 200 - assert any(h == (b"content-type", b"text/event-stream") for h in first_call["headers"]) + assert any( + h == (b"content-type", b"text/event-stream") for h in first_call["headers"] + ) # Second call: token streamer assert call_order == ["token_streamer"] @@ -169,6 +175,7 @@ async def test_streamer(send: Send): @pytest.mark.asyncio async def test_streaming_response_headers(self): """Verify content-type headers for event-streaming.""" + async def mock_streamer(send: Send): pass @@ -181,12 +188,13 @@ async def mock_streamer(send: Send): # Check that headers include text/event-stream headers_call = send_mock.call_args_list[0][0][0] headers = headers_call["headers"] - + assert (b"content-type", b"text/event-stream") in headers @pytest.mark.asyncio async def test_asgi_interface_compatibility(self): """Test ASGI scope/receive/send interface.""" + async def mock_streamer(send: Send): await send({"type": "http.response.body", "body": b"test"}) @@ -506,7 +514,7 @@ async def simple_streamer(send: Send): # Check headers in first call headers_call = send_mock.call_args_list[0][0][0] headers = dict(headers_call["headers"]) - + # Verify event-stream header is present assert headers.get(b"content-type") == b"text/event-stream" @@ -521,7 +529,7 @@ async def streamer2(send: Send): await send({"type": "http.response.body", "body": b"stream2"}) synapse = ConcreteStreamingSynapse() - + response1 = synapse.create_streaming_response(streamer1) response2 = synapse.create_streaming_response(streamer2) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index da389445fb..a12d43278e 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -17,6 +17,7 @@ from bittensor.core.axon import Axon from bittensor.core.chain_data import SubnetHyperparameters, SelectiveMetagraphIndex from bittensor.core.settings import ( + DEFAULT_MEV_PROTECTION, DEFAULT_MEV_PROTECTION, DEFAULT_PERIOD, version_as_int, @@ -2772,10 +2773,11 @@ def test_unstake_success(mocker, subtensor, fake_wallet): safe_unstaking=False, allow_partial_stake=False, rate_tolerance=0.005, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, + raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, - raise_error=False, ) assert result == mock_unstake_extrinsic.return_value @@ -2812,6 +2814,7 @@ def test_unstake_with_safe_unstaking(mocker, subtensor, fake_wallet): safe_unstaking=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, + mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, wait_for_inclusion=True, From 248894361b03821bdef15b18dda25118e694c748 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 20:09:05 -0800 Subject: [PATCH 45/67] add new sync unit tests --- tests/unit_tests/test_subtensor.py | 465 ++++++++++++++++++++++++++++- 1 file changed, 458 insertions(+), 7 deletions(-) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index a12d43278e..82c9e1bf7c 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1,12 +1,12 @@ import argparse import datetime import unittest.mock as mock -from unittest.mock import MagicMock, ANY +from unittest.mock import ANY, MagicMock import pytest import websockets from async_substrate_interface import sync_substrate -from async_substrate_interface.types import ScaleObj, Runtime +from async_substrate_interface.types import Runtime, ScaleObj from bittensor_wallet import Wallet from scalecodec import GenericCall @@ -15,21 +15,19 @@ from bittensor.core import subtensor as subtensor_module from bittensor.core.async_subtensor import AsyncSubtensor, logging from bittensor.core.axon import Axon -from bittensor.core.chain_data import SubnetHyperparameters, SelectiveMetagraphIndex +from bittensor.core.chain_data import SelectiveMetagraphIndex, SubnetHyperparameters from bittensor.core.settings import ( - DEFAULT_MEV_PROTECTION, DEFAULT_MEV_PROTECTION, DEFAULT_PERIOD, version_as_int, ) from bittensor.core.subtensor import Subtensor -from bittensor.core.types import AxonServeCallParams -from bittensor.core.types import ExtrinsicResponse +from bittensor.core.types import AxonServeCallParams, ExtrinsicResponse from bittensor.utils import ( Certificate, + determine_chain_endpoint_and_network, u16_normalized_float, u64_normalized_float, - determine_chain_endpoint_and_network, ) from bittensor.utils.balance import Balance @@ -5940,3 +5938,456 @@ def test_get_stake_info_for_coldkeys_success(subtensor, mocker): mocked_stake_info_list_from_dicts.assert_has_calls( [mocker.call([stake_info_dict_1]), mocker.call([stake_info_dict_2])] ) + + +def test_get_mev_shield_current_key_success(subtensor, mocker): + """Test get_mev_shield_current_key returns correct key when found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_public_key_bytes = b"\x00" * 1184 # ML-KEM-768 public key size + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query = mocker.patch.object(subtensor.substrate, "query") + mocked_query.return_value = iter([fake_public_key_bytes]) + + # Call + result = subtensor.get_mev_shield_current_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query.assert_called_once_with( + module="MevShield", + storage_function="CurrentKey", + block_hash=fake_block_hash, + ) + assert result == fake_public_key_bytes + + +def test_get_mev_shield_current_key_none(subtensor, mocker): + """Test get_mev_shield_current_key returns None when key not found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query = mocker.patch.object(subtensor.substrate, "query", return_value=None) + + # Call + result = subtensor.get_mev_shield_current_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query.assert_called_once_with( + module="MevShield", + storage_function="CurrentKey", + block_hash=fake_block_hash, + ) + assert result is None + + +def test_get_mev_shield_current_key_invalid_size(subtensor, mocker): + """Test get_mev_shield_current_key raises ValueError for invalid key size.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_public_key_bytes = b"\x00" * 1000 # Invalid size + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query = mocker.patch.object(subtensor.substrate, "query") + mocked_query.return_value = iter([fake_public_key_bytes]) + + # Call & Assert + with pytest.raises(ValueError, match="Invalid ML-KEM-768 public key size"): + subtensor.get_mev_shield_current_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query.assert_called_once_with( + module="MevShield", + storage_function="CurrentKey", + block_hash=fake_block_hash, + ) + + +def test_get_mev_shield_next_key_success(subtensor, mocker): + """Test get_mev_shield_next_key returns correct key when found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_public_key_bytes = b"\x00" * 1184 # ML-KEM-768 public key size + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query = mocker.patch.object(subtensor.substrate, "query") + mocked_query.return_value = iter([fake_public_key_bytes]) + + # Call + result = subtensor.get_mev_shield_next_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query.assert_called_once_with( + module="MevShield", + storage_function="NextKey", + block_hash=fake_block_hash, + ) + assert result == fake_public_key_bytes + + +def test_get_mev_shield_next_key_none(subtensor, mocker): + """Test get_mev_shield_next_key returns None when key not found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query = mocker.patch.object(subtensor.substrate, "query", return_value=None) + + # Call + result = subtensor.get_mev_shield_next_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query.assert_called_once_with( + module="MevShield", + storage_function="NextKey", + block_hash=fake_block_hash, + ) + assert result is None + + +def test_get_mev_shield_next_key_invalid_size(subtensor, mocker): + """Test get_mev_shield_next_key raises ValueError for invalid key size.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_public_key_bytes = b"\x00" * 1000 # Invalid size + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query = mocker.patch.object(subtensor.substrate, "query") + mocked_query.return_value = iter([fake_public_key_bytes]) + + # Call & Assert + with pytest.raises(ValueError, match="Invalid ML-KEM-768 public key size"): + subtensor.get_mev_shield_next_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query.assert_called_once_with( + module="MevShield", + storage_function="NextKey", + block_hash=fake_block_hash, + ) + + +def test_get_mev_shield_submission_success(subtensor, mocker): + """Test get_mev_shield_submission returns correct submission when found.""" + # Prep + fake_submission_id = "0x1234567890abcdef" + fake_block = 123 + fake_block_hash = "0x123abc" + fake_author = b"\x01" * 32 + fake_commitment = b"\x02" * 32 + fake_ciphertext = b"\x03" * 100 + fake_submitted_in = 100 + + fake_query_result = { + "author": [fake_author], + "commitment": [fake_commitment], + "ciphertext": [fake_ciphertext], + "submitted_in": fake_submitted_in, + } + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query = mocker.patch.object( + subtensor.substrate, "query", return_value=fake_query_result + ) + mocked_decode_account_id = mocker.patch.object( + subtensor_module, + "decode_account_id", + return_value="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ) + + # Call + result = subtensor.get_mev_shield_submission( + submission_id=fake_submission_id, block=fake_block + ) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query.assert_called_once_with( + module="MevShield", + storage_function="Submissions", + params=[bytes.fromhex("1234567890abcdef")], + block_hash=fake_block_hash, + ) + mocked_decode_account_id.assert_called_once_with([fake_author]) + assert result == { + "author": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "commitment": fake_commitment, + "ciphertext": fake_ciphertext, + "submitted_in": fake_submitted_in, + } + + +def test_get_mev_shield_submission_without_0x_prefix(subtensor, mocker): + """Test get_mev_shield_submission handles submission_id without 0x prefix.""" + # Prep + fake_submission_id = "1234567890abcdef" + fake_block = 123 + fake_block_hash = "0x123abc" + fake_query_result = { + "author": [b"\x01" * 32], + "commitment": [b"\x02" * 32], + "ciphertext": [b"\x03" * 100], + "submitted_in": 100, + } + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query = mocker.patch.object( + subtensor.substrate, "query", return_value=fake_query_result + ) + mocked_decode_account_id = mocker.patch.object( + subtensor_module, + "decode_account_id", + return_value="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ) + + # Call + result = subtensor.get_mev_shield_submission( + submission_id=fake_submission_id, block=fake_block + ) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query.assert_called_once_with( + module="MevShield", + storage_function="Submissions", + params=[bytes.fromhex("1234567890abcdef")], + block_hash=fake_block_hash, + ) + mocked_decode_account_id.assert_called_once_with([b"\x01" * 32]) + assert result is not None + + +def test_get_mev_shield_submission_none(subtensor, mocker): + """Test get_mev_shield_submission returns None when submission not found.""" + # Prep + fake_submission_id = "0x1234567890abcdef" + fake_block = 123 + fake_block_hash = "0x123abc" + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query = mocker.patch.object(subtensor.substrate, "query", return_value=None) + + # Call + result = subtensor.get_mev_shield_submission( + submission_id=fake_submission_id, block=fake_block + ) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query.assert_called_once_with( + module="MevShield", + storage_function="Submissions", + params=[bytes.fromhex("1234567890abcdef")], + block_hash=fake_block_hash, + ) + assert result is None + + +def test_get_mev_shield_submissions_success(subtensor, mocker): + """Test get_mev_shield_submissions returns all submissions when found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_submission_id_1 = b"\x01" * 32 + fake_submission_id_2 = b"\x02" * 32 + fake_author_1 = b"\x03" * 32 + fake_author_2 = b"\x04" * 32 + fake_commitment_1 = b"\x05" * 32 + fake_commitment_2 = b"\x06" * 32 + fake_ciphertext_1 = b"\x07" * 100 + fake_ciphertext_2 = b"\x08" * 100 + + fake_query_result = mocker.MagicMock() + fake_query_result.__iter__.return_value = iter( + [ + ( + [fake_submission_id_1], + mocker.MagicMock( + value={ + "author": [fake_author_1], + "commitment": [fake_commitment_1], + "ciphertext": [fake_ciphertext_1], + "submitted_in": 100, + } + ), + ), + ( + [fake_submission_id_2], + mocker.MagicMock( + value={ + "author": [fake_author_2], + "commitment": [fake_commitment_2], + "ciphertext": [fake_ciphertext_2], + "submitted_in": 101, + } + ), + ), + ] + ) + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query_map = mocker.patch.object( + subtensor.substrate, "query_map", return_value=fake_query_result + ) + mocked_decode_account_id = mocker.patch.object( + subtensor_module, + "decode_account_id", + side_effect=[ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + ], + ) + + # Call + result = subtensor.get_mev_shield_submissions(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query_map.assert_called_once_with( + module="MevShield", + storage_function="Submissions", + block_hash=fake_block_hash, + ) + assert result is not None + assert len(result) == 2 + assert "0x" + fake_submission_id_1.hex() in result + assert "0x" + fake_submission_id_2.hex() in result + assert result["0x" + fake_submission_id_1.hex()]["submitted_in"] == 100 + assert result["0x" + fake_submission_id_2.hex()]["submitted_in"] == 101 + # Verify decode_account_id was called for both submissions + assert mocked_decode_account_id.call_count == 2 + + +def test_get_mev_shield_submissions_none(subtensor, mocker): + """Test get_mev_shield_submissions returns None when no submissions found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + + fake_query_result = mocker.MagicMock() + fake_query_result.__iter__.return_value = iter([]) + + mocked_determine_block_hash = mocker.patch.object( + subtensor, "determine_block_hash", return_value=fake_block_hash + ) + mocked_query_map = mocker.patch.object( + subtensor.substrate, "query_map", return_value=fake_query_result + ) + + # Call + result = subtensor.get_mev_shield_submissions(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_called_once_with(block=fake_block) + mocked_query_map.assert_called_once_with( + module="MevShield", + storage_function="Submissions", + block_hash=fake_block_hash, + ) + assert result is None + + +def test_mev_submit_encrypted_success(subtensor, fake_wallet, mocker): + """Test mev_submit_encrypted calls submit_encrypted_extrinsic correctly.""" + # Prep + fake_call = mocker.Mock(spec=GenericCall) + fake_signer_keypair = mocker.Mock() + fake_period = 128 + fake_raise_error = False + fake_wait_for_inclusion = True + fake_wait_for_finalization = True + fake_wait_for_revealed_execution = True + fake_blocks_for_revealed_execution = 5 + + mocked_submit_encrypted_extrinsic = mocker.patch.object( + subtensor_module, "submit_encrypted_extrinsic" + ) + + # Call + result = subtensor.mev_submit_encrypted( + wallet=fake_wallet, + call=fake_call, + signer_keypair=fake_signer_keypair, + period=fake_period, + raise_error=fake_raise_error, + wait_for_inclusion=fake_wait_for_inclusion, + wait_for_finalization=fake_wait_for_finalization, + wait_for_revealed_execution=fake_wait_for_revealed_execution, + blocks_for_revealed_execution=fake_blocks_for_revealed_execution, + ) + + # Asserts + mocked_submit_encrypted_extrinsic.assert_called_once_with( + subtensor=subtensor, + wallet=fake_wallet, + call=fake_call, + signer_keypair=fake_signer_keypair, + period=fake_period, + raise_error=fake_raise_error, + wait_for_inclusion=fake_wait_for_inclusion, + wait_for_finalization=fake_wait_for_finalization, + wait_for_revealed_execution=fake_wait_for_revealed_execution, + blocks_for_revealed_execution=fake_blocks_for_revealed_execution, + ) + assert result == mocked_submit_encrypted_extrinsic.return_value + + +def test_mev_submit_encrypted_default_params(subtensor, fake_wallet, mocker): + """Test mev_submit_encrypted with default parameters.""" + # Prep + fake_call = mocker.Mock(spec=GenericCall) + + mocked_submit_encrypted_extrinsic = mocker.patch.object( + subtensor_module, "submit_encrypted_extrinsic" + ) + + # Call + result = subtensor.mev_submit_encrypted(wallet=fake_wallet, call=fake_call) + + # Asserts + mocked_submit_encrypted_extrinsic.assert_called_once_with( + subtensor=subtensor, + wallet=fake_wallet, + call=fake_call, + signer_keypair=None, + period=DEFAULT_PERIOD, + raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, + wait_for_revealed_execution=True, + blocks_for_revealed_execution=5, + ) + assert result == mocked_submit_encrypted_extrinsic.return_value From a06811302263f669599185900f22d2d9574b6d91 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 20:31:22 -0800 Subject: [PATCH 46/67] add new async unit tests --- tests/unit_tests/test_async_subtensor.py | 468 ++++++++++++++++++++++- 1 file changed, 464 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index a2c06022fd..f671ac8147 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2,7 +2,7 @@ import unittest.mock as mock import pytest -from async_substrate_interface.types import ScaleObj, Runtime +from async_substrate_interface.types import Runtime, ScaleObj from bittensor_wallet import Wallet from scalecodec import GenericCall @@ -10,15 +10,15 @@ from bittensor.core import async_subtensor, settings from bittensor.core.async_subtensor import AsyncSubtensor from bittensor.core.chain_data import ( - proposal_vote_data, ChainIdentity, NeuronInfo, - StakeInfo, SelectiveMetagraphIndex, + StakeInfo, + proposal_vote_data, ) from bittensor.core.errors import BalanceTypeError -from bittensor.core.types import ExtrinsicResponse from bittensor.core.settings import DEFAULT_MEV_PROTECTION, DEFAULT_PERIOD +from bittensor.core.types import ExtrinsicResponse from bittensor.utils import U64_MAX, get_function_name from bittensor.utils.balance import Balance @@ -5827,3 +5827,463 @@ async def test_get_stake_info_for_coldkeys_success(subtensor, mocker): mocked_stake_info_list_from_dicts.assert_has_calls( [mocker.call([stake_info_dict_1]), mocker.call([stake_info_dict_2])] ) + + +@pytest.mark.asyncio +async def test_get_mev_shield_current_key_success(subtensor, mocker): + """Test get_mev_shield_current_key returns correct key when found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_public_key_bytes = b"\x00" * 1184 # ML-KEM-768 public key size + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query = mocker.AsyncMock() + mocked_query.return_value = iter([fake_public_key_bytes]) + subtensor.substrate.query = mocked_query + + # Call + result = await subtensor.get_mev_shield_current_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query.assert_awaited_once_with( + module="MevShield", + storage_function="CurrentKey", + block_hash=fake_block_hash, + ) + assert result == fake_public_key_bytes + + +@pytest.mark.asyncio +async def test_get_mev_shield_current_key_none(subtensor, mocker): + """Test get_mev_shield_current_key returns None when key not found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query = mocker.AsyncMock(return_value=None) + subtensor.substrate.query = mocked_query + + # Call + result = await subtensor.get_mev_shield_current_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query.assert_awaited_once_with( + module="MevShield", + storage_function="CurrentKey", + block_hash=fake_block_hash, + ) + assert result is None + + +@pytest.mark.asyncio +async def test_get_mev_shield_current_key_invalid_size(subtensor, mocker): + """Test get_mev_shield_current_key raises ValueError for invalid key size.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_public_key_bytes = b"\x00" * 1000 # Invalid size + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query = mocker.AsyncMock() + mocked_query.return_value = iter([fake_public_key_bytes]) + subtensor.substrate.query = mocked_query + + # Call & Assert + with pytest.raises(ValueError, match="Invalid ML-KEM-768 public key size"): + await subtensor.get_mev_shield_current_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query.assert_awaited_once_with( + module="MevShield", + storage_function="CurrentKey", + block_hash=fake_block_hash, + ) + + +@pytest.mark.asyncio +async def test_get_mev_shield_next_key_success(subtensor, mocker): + """Test get_mev_shield_next_key returns correct key when found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_public_key_bytes = b"\x00" * 1184 # ML-KEM-768 public key size + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query = mocker.AsyncMock() + mocked_query.return_value = iter([fake_public_key_bytes]) + subtensor.substrate.query = mocked_query + + # Call + result = await subtensor.get_mev_shield_next_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query.assert_awaited_once_with( + module="MevShield", + storage_function="NextKey", + block_hash=fake_block_hash, + ) + assert result == fake_public_key_bytes + + +@pytest.mark.asyncio +async def test_get_mev_shield_next_key_none(subtensor, mocker): + """Test get_mev_shield_next_key returns None when key not found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query = mocker.AsyncMock(return_value=None) + subtensor.substrate.query = mocked_query + + # Call + result = await subtensor.get_mev_shield_next_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query.assert_awaited_once_with( + module="MevShield", + storage_function="NextKey", + block_hash=fake_block_hash, + ) + assert result is None + + +@pytest.mark.asyncio +async def test_get_mev_shield_next_key_invalid_size(subtensor, mocker): + """Test get_mev_shield_next_key raises ValueError for invalid key size.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_public_key_bytes = b"\x00" * 1000 # Invalid size + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query = mocker.AsyncMock() + mocked_query.return_value = iter([fake_public_key_bytes]) + subtensor.substrate.query = mocked_query + + # Call & Assert + with pytest.raises(ValueError, match="Invalid ML-KEM-768 public key size"): + await subtensor.get_mev_shield_next_key(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query.assert_awaited_once_with( + module="MevShield", + storage_function="NextKey", + block_hash=fake_block_hash, + ) + + +@pytest.mark.asyncio +async def test_get_mev_shield_submission_success(subtensor, mocker): + """Test get_mev_shield_submission returns correct submission when found.""" + # Prep + fake_submission_id = "0x1234567890abcdef" + fake_block = 123 + fake_block_hash = "0x123abc" + fake_author = b"\x01" * 32 + fake_commitment = b"\x02" * 32 + fake_ciphertext = b"\x03" * 100 + fake_submitted_in = 100 + + fake_query_result = { + "author": [fake_author], + "commitment": [fake_commitment], + "ciphertext": [fake_ciphertext], + "submitted_in": fake_submitted_in, + } + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query = mocker.AsyncMock(return_value=fake_query_result) + subtensor.substrate.query = mocked_query + mocked_decode_account_id = mocker.patch.object( + async_subtensor, + "decode_account_id", + return_value="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ) + + # Call + result = await subtensor.get_mev_shield_submission( + submission_id=fake_submission_id, block=fake_block + ) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query.assert_awaited_once_with( + module="MevShield", + storage_function="Submissions", + params=[bytes.fromhex("1234567890abcdef")], + block_hash=fake_block_hash, + ) + mocked_decode_account_id.assert_called_once_with([fake_author]) + assert result == { + "author": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "commitment": fake_commitment, + "ciphertext": fake_ciphertext, + "submitted_in": fake_submitted_in, + } + + +@pytest.mark.asyncio +async def test_get_mev_shield_submission_without_0x_prefix(subtensor, mocker): + """Test get_mev_shield_submission handles submission_id without 0x prefix.""" + # Prep + fake_submission_id = "1234567890abcdef" + fake_block = 123 + fake_block_hash = "0x123abc" + fake_query_result = { + "author": [b"\x01" * 32], + "commitment": [b"\x02" * 32], + "ciphertext": [b"\x03" * 100], + "submitted_in": 100, + } + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query = mocker.AsyncMock(return_value=fake_query_result) + subtensor.substrate.query = mocked_query + mocked_decode_account_id = mocker.patch.object( + async_subtensor, + "decode_account_id", + return_value="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ) + + # Call + result = await subtensor.get_mev_shield_submission( + submission_id=fake_submission_id, block=fake_block + ) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query.assert_awaited_once_with( + module="MevShield", + storage_function="Submissions", + params=[bytes.fromhex("1234567890abcdef")], + block_hash=fake_block_hash, + ) + mocked_decode_account_id.assert_called_once_with([b"\x01" * 32]) + assert result is not None + + +@pytest.mark.asyncio +async def test_get_mev_shield_submission_none(subtensor, mocker): + """Test get_mev_shield_submission returns None when submission not found.""" + # Prep + fake_submission_id = "0x1234567890abcdef" + fake_block = 123 + fake_block_hash = "0x123abc" + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query = mocker.AsyncMock(return_value=None) + subtensor.substrate.query = mocked_query + + # Call + result = await subtensor.get_mev_shield_submission( + submission_id=fake_submission_id, block=fake_block + ) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query.assert_awaited_once_with( + module="MevShield", + storage_function="Submissions", + params=[bytes.fromhex("1234567890abcdef")], + block_hash=fake_block_hash, + ) + assert result is None + + +@pytest.mark.asyncio +async def test_get_mev_shield_submissions_success(subtensor, mocker): + """Test get_mev_shield_submissions returns all submissions when found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + fake_submission_id_1 = b"\x01" * 32 + fake_submission_id_2 = b"\x02" * 32 + fake_author_1 = b"\x03" * 32 + fake_author_2 = b"\x04" * 32 + fake_commitment_1 = b"\x05" * 32 + fake_commitment_2 = b"\x06" * 32 + fake_ciphertext_1 = b"\x07" * 100 + fake_ciphertext_2 = b"\x08" * 100 + + fake_query_result = mocker.AsyncMock() + fake_query_result.__aiter__.return_value = iter( + [ + ( + [fake_submission_id_1], + mocker.MagicMock( + value={ + "author": [fake_author_1], + "commitment": [fake_commitment_1], + "ciphertext": [fake_ciphertext_1], + "submitted_in": 100, + } + ), + ), + ( + [fake_submission_id_2], + mocker.MagicMock( + value={ + "author": [fake_author_2], + "commitment": [fake_commitment_2], + "ciphertext": [fake_ciphertext_2], + "submitted_in": 101, + } + ), + ), + ] + ) + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query_map = mocker.AsyncMock(return_value=fake_query_result) + subtensor.substrate.query_map = mocked_query_map + mocked_decode_account_id = mocker.patch.object( + async_subtensor, + "decode_account_id", + side_effect=[ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + ], + ) + + # Call + result = await subtensor.get_mev_shield_submissions(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query_map.assert_awaited_once_with( + module="MevShield", + storage_function="Submissions", + block_hash=fake_block_hash, + ) + assert result is not None + assert len(result) == 2 + assert "0x" + fake_submission_id_1.hex() in result + assert "0x" + fake_submission_id_2.hex() in result + assert result["0x" + fake_submission_id_1.hex()]["submitted_in"] == 100 + assert result["0x" + fake_submission_id_2.hex()]["submitted_in"] == 101 + # Verify decode_account_id was called for both submissions + assert mocked_decode_account_id.call_count == 2 + + +@pytest.mark.asyncio +async def test_get_mev_shield_submissions_none(subtensor, mocker): + """Test get_mev_shield_submissions returns None when no submissions found.""" + # Prep + fake_block = 123 + fake_block_hash = "0x123abc" + + fake_query_result = mocker.AsyncMock() + fake_query_result.__aiter__.return_value = iter([]) + + mocked_determine_block_hash = mocker.AsyncMock(return_value=fake_block_hash) + mocker.patch.object(subtensor, "determine_block_hash", mocked_determine_block_hash) + mocked_query_map = mocker.AsyncMock(return_value=fake_query_result) + subtensor.substrate.query_map = mocked_query_map + + # Call + result = await subtensor.get_mev_shield_submissions(block=fake_block) + + # Asserts + mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False) + mocked_query_map.assert_awaited_once_with( + module="MevShield", + storage_function="Submissions", + block_hash=fake_block_hash, + ) + assert result is None + + +@pytest.mark.asyncio +async def test_mev_submit_encrypted_success(subtensor, fake_wallet, mocker): + """Test mev_submit_encrypted calls submit_encrypted_extrinsic correctly.""" + # Prep + fake_call = mocker.Mock(spec=GenericCall) + fake_signer_keypair = mocker.Mock() + fake_period = 128 + fake_raise_error = False + fake_wait_for_inclusion = True + fake_wait_for_finalization = True + fake_wait_for_revealed_execution = True + fake_blocks_for_revealed_execution = 5 + + mocked_submit_encrypted_extrinsic = mocker.AsyncMock() + mocker.patch.object( + async_subtensor, "submit_encrypted_extrinsic", mocked_submit_encrypted_extrinsic + ) + + # Call + result = await subtensor.mev_submit_encrypted( + wallet=fake_wallet, + call=fake_call, + signer_keypair=fake_signer_keypair, + period=fake_period, + raise_error=fake_raise_error, + wait_for_inclusion=fake_wait_for_inclusion, + wait_for_finalization=fake_wait_for_finalization, + wait_for_revealed_execution=fake_wait_for_revealed_execution, + blocks_for_revealed_execution=fake_blocks_for_revealed_execution, + ) + + # Asserts + mocked_submit_encrypted_extrinsic.assert_awaited_once_with( + subtensor=subtensor, + wallet=fake_wallet, + call=fake_call, + signer_keypair=fake_signer_keypair, + period=fake_period, + raise_error=fake_raise_error, + wait_for_inclusion=fake_wait_for_inclusion, + wait_for_finalization=fake_wait_for_finalization, + wait_for_revealed_execution=fake_wait_for_revealed_execution, + blocks_for_revealed_execution=fake_blocks_for_revealed_execution, + ) + assert result == mocked_submit_encrypted_extrinsic.return_value + + +@pytest.mark.asyncio +async def test_mev_submit_encrypted_default_params(subtensor, fake_wallet, mocker): + """Test mev_submit_encrypted with default parameters.""" + # Prep + fake_call = mocker.Mock(spec=GenericCall) + + mocked_submit_encrypted_extrinsic = mocker.AsyncMock() + mocker.patch.object( + async_subtensor, "submit_encrypted_extrinsic", mocked_submit_encrypted_extrinsic + ) + + # Call + result = await subtensor.mev_submit_encrypted(wallet=fake_wallet, call=fake_call) + + # Asserts + mocked_submit_encrypted_extrinsic.assert_awaited_once_with( + subtensor=subtensor, + wallet=fake_wallet, + call=fake_call, + signer_keypair=None, + period=DEFAULT_PERIOD, + raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, + wait_for_revealed_execution=True, + blocks_for_revealed_execution=5, + ) + assert result == mocked_submit_encrypted_extrinsic.return_value From 5ac4d3ebdfa484eb006c3da52876e4477ecfc4a2 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 20:42:55 -0800 Subject: [PATCH 47/67] improve `test_mev_shield_happy_path` logic --- bittensor/core/extrinsics/asyncex/mev_shield.py | 6 ++++++ bittensor/core/extrinsics/mev_shield.py | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index 2a9b863374..7469b531b3 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -143,6 +143,12 @@ async def submit_encrypted_extrinsic( ).success: return unlocked + if wait_for_revealed_execution and not wait_for_inclusion: + return ExtrinsicResponse.from_exception( + raise_error=raise_error, + error=ValueError("`wait_for_inclusion` must be `True` if `wait_for_revealed_execution` is `True`.") + ) + # Use wallet.coldkey as default signer if signer_keypair is not provided if signer_keypair is None: signer_keypair = wallet.coldkey diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index 9d6e99a7aa..ec777354a6 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -143,6 +143,11 @@ def submit_encrypted_extrinsic( ).success: return unlocked + if wait_for_revealed_execution and not wait_for_inclusion: + return ExtrinsicResponse.from_exception( + raise_error=raise_error, error=ValueError("`wait_for_inclusion` must be `True` if `wait_for_revealed_execution` is `True`.") + ) + # Use wallet.coldkey as default signer if signer_keypair is not provided if signer_keypair is None: signer_keypair = wallet.coldkey From 54cd4782463f52f5676cd490878158f30f9bc195 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 21:01:45 -0800 Subject: [PATCH 48/67] add check in `submit_encrypted_extrinsic` --- bittensor/core/extrinsics/asyncex/mev_shield.py | 4 +++- bittensor/core/extrinsics/mev_shield.py | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index 7469b531b3..7cfc3638f5 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -146,7 +146,9 @@ async def submit_encrypted_extrinsic( if wait_for_revealed_execution and not wait_for_inclusion: return ExtrinsicResponse.from_exception( raise_error=raise_error, - error=ValueError("`wait_for_inclusion` must be `True` if `wait_for_revealed_execution` is `True`.") + error=ValueError( + "`wait_for_inclusion` must be `True` if `wait_for_revealed_execution` is `True`." + ), ) # Use wallet.coldkey as default signer if signer_keypair is not provided diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index ec777354a6..993fa03c19 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -145,7 +145,10 @@ def submit_encrypted_extrinsic( if wait_for_revealed_execution and not wait_for_inclusion: return ExtrinsicResponse.from_exception( - raise_error=raise_error, error=ValueError("`wait_for_inclusion` must be `True` if `wait_for_revealed_execution` is `True`.") + raise_error=raise_error, + error=ValueError( + "`wait_for_inclusion` must be `True` if `wait_for_revealed_execution` is `True`." + ), ) # Use wallet.coldkey as default signer if signer_keypair is not provided @@ -196,8 +199,8 @@ def submit_encrypted_extrinsic( if wait_for_revealed_execution: triggered_events = response.extrinsic_receipt.triggered_events event_hash_id = get_event_attributes_by_event_name( - events=triggered_events, - event_name="mevShield.EncryptedSubmitted", # type: ignore + events=triggered_events, # type: ignore + event_name="mevShield.EncryptedSubmitted", )["attributes"]["id"] revealed_extrinsic = find_revealed_extrinsic( From 08cf924d708f446ec958a7267f6b6efd6d3ddbf2 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 21:01:58 -0800 Subject: [PATCH 49/67] non-fast-block e2e test --- tests/e2e_tests/test_mev_shield.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_mev_shield.py b/tests/e2e_tests/test_mev_shield.py index ac5ff1c12c..d54934599d 100644 --- a/tests/e2e_tests/test_mev_shield.py +++ b/tests/e2e_tests/test_mev_shield.py @@ -18,7 +18,7 @@ TEMPO_TO_SET = 3 -# @pytest.mark.parametrize("local_chain", [False], indirect=True) +@pytest.mark.parametrize("local_chain", [False], indirect=True) def test_mev_shield_happy_path( subtensor, alice_wallet, bob_wallet, charlie_wallet, dave_wallet, local_chain ): From 6d1a945b46d63f8d1ac66b0e46db6b2b7f6c5e0c Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 21:13:54 -0800 Subject: [PATCH 50/67] update ExtrinsicResponse.__str__ --- bittensor/core/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bittensor/core/types.py b/bittensor/core/types.py index 0ab4a02b20..f45666c688 100644 --- a/bittensor/core/types.py +++ b/bittensor/core/types.py @@ -383,9 +383,10 @@ def __str__(self): f"\textrinsic: {self.extrinsic}\n" f"\textrinsic_fee: {self.extrinsic_fee}\n" f"\textrinsic_receipt: {_extrinsic_receipt}" + f"\tmev_extrinsic_receipt: {self.mev_extrinsic_receipt}" f"\ttransaction_tao_fee: {self.transaction_tao_fee}\n" f"\ttransaction_alpha_fee: {self.transaction_alpha_fee}\n" - f"\terror: {self.error}" + f"\terror: {self.error}\n" f"\tdata: {self.data}\n" ) From 2877d2d9a190571017bdcca03da3061b1556e2ea Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 21:27:07 -0800 Subject: [PATCH 51/67] update ExtrinsicResponse.__str__ --- bittensor/core/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/types.py b/bittensor/core/types.py index f45666c688..aae3bafb5d 100644 --- a/bittensor/core/types.py +++ b/bittensor/core/types.py @@ -382,8 +382,8 @@ def __str__(self): f"\textrinsic_function: {self.extrinsic_function}\n" f"\textrinsic: {self.extrinsic}\n" f"\textrinsic_fee: {self.extrinsic_fee}\n" - f"\textrinsic_receipt: {_extrinsic_receipt}" - f"\tmev_extrinsic_receipt: {self.mev_extrinsic_receipt}" + f"\textrinsic_receipt: {_extrinsic_receipt}\n" + f"\tmev_extrinsic_receipt: {self.mev_extrinsic_receipt}\n" f"\ttransaction_tao_fee: {self.transaction_tao_fee}\n" f"\ttransaction_alpha_fee: {self.transaction_alpha_fee}\n" f"\terror: {self.error}\n" From 44116880113f00ceeb8655e0ba16e789bf5dcc94 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 2 Dec 2025 21:28:23 -0800 Subject: [PATCH 52/67] bumping `bittensor-drand` version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 430cd916d2..3f95468c9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ dependencies = [ "pydantic>=2.3,<3", "scalecodec==1.2.12", "uvicorn", - "bittensor-drand>=1.0.0,<2.0.0", + "bittensor-drand>=1.2.0,<2.0.0", "bittensor-wallet>=4.0.0,<5.0", "async-substrate-interface>=1.5.13" ] From 381a32bf86c4637ec8193d8b72aa08545016b4f0 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Wed, 3 Dec 2025 14:49:19 -0800 Subject: [PATCH 53/67] add `post_process_mev_response` --- bittensor/core/extrinsics/utils.py | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index ab543dce35..7a7e80fc41 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -17,6 +17,17 @@ from bittensor.core.subtensor import Subtensor from scalecodec.types import GenericCall from bittensor_wallet.keypair import Keypair + from async_substrate_interface import AsyncExtrinsicReceipt, ExtrinsicReceipt + + +MEV_SUBMITTED_EVENT = "mevShield.EncryptedSubmitted" +MEV_EXECUTED_EVENT = "mevShield.DecryptedExecuted" +MEV_UNSUCCESSFUL_EVENTS = [ + "mevShield.DecryptedRejected", + "mevShield.DecryptionFailed", +] + +POST_SUBMIT_MEV_EVENTS = [MEV_EXECUTED_EVENT] + MEV_UNSUCCESSFUL_EVENTS def get_old_stakes( @@ -317,3 +328,54 @@ def get_event_attributes_by_event_name(events: list, event_name: str) -> Optiona ): return event return None + + +def post_process_mev_response( + response: "ExtrinsicResponse", + revealed_name: str, + revealed_extrinsic: Optional["ExtrinsicReceipt | AsyncExtrinsicReceipt"], + raise_error: bool = False, +) -> None: + """ + Post-processes the result of a MEV Shield extrinsic submission by updating the response object based on the revealed + extrinsic execution status. + + This function analyzes the revealed extrinsic (execute_revealed) that was found after the initial encrypted + submission and updates the response object accordingly. It handles cases where the revealed extrinsic was not found, + where it failed (DecryptedRejected or DecryptionFailed events), and propagates errors if requested. + + Parameters: + response: The ExtrinsicResponse object from the initial submit_encrypted call. This object will be modified + in-place with the revealed extrinsic receipt and updated success/error status. + revealed_name: The name of the event found in the revealed extrinsic (e.g., "mevShield.DecryptedExecuted", + "mevShield.DecryptedRejected", "mevShield.DecryptionFailed"). + revealed_extrinsic: The ExtrinsicReceipt or AsyncExtrinsicReceipt object for the execute_revealed transaction, + if found. None if the revealed extrinsic was not found in the expected blocks. This receipt contains the + triggered events and execution details. + raise_error: If True, raises the error immediately if the response contains an error. If False, the error is + stored in response.error but not raised. Defaults to False. + + Returns: + None. The function modifies the response object in-place by setting: + - response.mev_extrinsic_receipt: The revealed extrinsic receipt + - response.success: False if revealed extrinsic not found or failed, otherwise True. + - response.message: Error message describing the failure if failure. + - response.error: RuntimeError with the response.message. + """ + # add revealed extrinsic receipt to response + response.mev_extrinsic_receipt = revealed_extrinsic + + # when main extrinsic is successful but revealed extrinsic is not found in the chain. + if revealed_extrinsic is None: + response.success = False + response.message = "Result event not found in chain." + response.error = RuntimeError(response.message) + + # when main extrinsic is successful but revealed extrinsic is not successful. + if revealed_name in MEV_UNSUCCESSFUL_EVENTS: + response.success = False + response.message = f"{revealed_name}: Check `mev_extrinsic_receipt` for details." + response.error = RuntimeError(response.message) + + if response.error and raise_error: + raise response.error From 9c2e3449dfca12a1b1bcd1facfaff407c3d54436 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Wed, 3 Dec 2025 14:51:09 -0800 Subject: [PATCH 54/67] improve `submit_encrypted_extrinsic`, DRY, etc --- .../core/extrinsics/asyncex/mev_shield.py | 45 +++++++------------ bittensor/core/extrinsics/mev_shield.py | 44 ++++++------------ 2 files changed, 29 insertions(+), 60 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index 7cfc3638f5..d18ff5ff07 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -8,6 +8,9 @@ from bittensor.core.extrinsics.utils import ( get_event_attributes_by_event_name, get_mev_commitment_and_ciphertext, + post_process_mev_response, + POST_SUBMIT_MEV_EVENTS, + MEV_SUBMITTED_EVENT, ) from bittensor.core.types import ExtrinsicResponse from bittensor.utils.btlogging import logging @@ -20,7 +23,6 @@ async def find_revealed_extrinsic( subtensor: "AsyncSubtensor", - signer_ss58: str, event_names: list[str], event_hash_id: str, start_block_hash: str, @@ -35,8 +37,6 @@ async def find_revealed_extrinsic( Parameters: subtensor: The Subtensor instance used for blockchain queries. - signer_ss58: The SS58 address of the signer account. Used to verify that the event belongs to the correct - transaction (matches the "signer" attribute in the event). event_names: The event identifiers to search for. Typically "DecryptedExecuted" or "DecryptedRejected" for MEV Shield transactions. event_hash_id: The wrapper_id (hash of (author, commitment, ciphertext)) to match. This uniquely identifies a @@ -68,10 +68,7 @@ async def find_revealed_extrinsic( for event_name in event_names: if event := get_event_attributes_by_event_name(events, event_name): - if ( - event["attributes"]["signer"] == signer_ss58 - and event["attributes"]["id"] == event_hash_id - ): + if event["attributes"]["id"] == event_hash_id: return event_name, AsyncExtrinsicReceipt( substrate=subtensor.substrate, block_hash=current_block_hash, @@ -198,37 +195,25 @@ async def submit_encrypted_extrinsic( } if wait_for_revealed_execution: triggered_events = await response.extrinsic_receipt.triggered_events + event_hash_id = get_event_attributes_by_event_name( - events=triggered_events, event_name="mevShield.EncryptedSubmitted" + events=triggered_events, event_name=MEV_SUBMITTED_EVENT )["attributes"]["id"] - revealed_extrinsic = await find_revealed_extrinsic( + reveled_event, reveled_extrinsic = await find_revealed_extrinsic( subtensor=subtensor, - signer_ss58=signer_keypair.ss58_address, - event_names=[ - "mevShield.DecryptedExecuted", - "mevShield.DecryptedRejected", - ], + event_names=POST_SUBMIT_MEV_EVENTS, event_hash_id=event_hash_id, start_block_hash=response.extrinsic_receipt.block_hash, blocks_ahead=blocks_for_revealed_execution, ) - if revealed_extrinsic and isinstance(revealed_extrinsic, tuple): - event_name, response.mev_extrinsic_receipt = revealed_extrinsic - - if event_name == "mevShield.DecryptedRejected": - response.success = False - response.message = "DecryptedRejected event found." - return response.from_exception( - raise_error=raise_error, - error=RuntimeError(response.message), - ) - else: - response.success = False - response.error = RuntimeError(f"DecryptedExecuted event not found.") - return response.from_exception( - raise_error=raise_error, error=response.error - ) + + post_process_mev_response( + response=response, + revealed_name=reveled_event, + revealed_extrinsic=reveled_extrinsic, + raise_error=raise_error, + ) logging.debug("[green]Encrypted extrinsic submitted successfully.[/green]") else: diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index 993fa03c19..f965fbfecd 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -8,6 +8,9 @@ from bittensor.core.extrinsics.utils import ( get_event_attributes_by_event_name, get_mev_commitment_and_ciphertext, + post_process_mev_response, + POST_SUBMIT_MEV_EVENTS, + MEV_SUBMITTED_EVENT, ) from bittensor.core.types import ExtrinsicResponse from bittensor.utils.btlogging import logging @@ -20,7 +23,6 @@ def find_revealed_extrinsic( subtensor: "Subtensor", - signer_ss58: str, event_names: list[str], event_hash_id: str, start_block_hash: str, @@ -35,8 +37,6 @@ def find_revealed_extrinsic( Parameters: subtensor: The Subtensor instance used for blockchain queries. - signer_ss58: The SS58 address of the signer account. Used to verify that the event belongs to the correct - transaction (matches the "signer" attribute in the event). event_names: The event identifiers to search for. Typically "DecryptedExecuted" or "DecryptedRejected" for MEV Shield transactions. event_hash_id: The wrapper_id (hash of (author, commitment, ciphertext)) to match. This uniquely identifies a @@ -68,10 +68,7 @@ def find_revealed_extrinsic( for event_name in event_names: if event := get_event_attributes_by_event_name(events, event_name): - if ( - event["attributes"]["signer"] == signer_ss58 - and event["attributes"]["id"] == event_hash_id - ): + if event["attributes"]["id"] == event_hash_id: return event_name, ExtrinsicReceipt( substrate=subtensor.substrate, block_hash=current_block_hash, @@ -200,36 +197,23 @@ def submit_encrypted_extrinsic( triggered_events = response.extrinsic_receipt.triggered_events event_hash_id = get_event_attributes_by_event_name( events=triggered_events, # type: ignore - event_name="mevShield.EncryptedSubmitted", + event_name=MEV_SUBMITTED_EVENT, )["attributes"]["id"] - revealed_extrinsic = find_revealed_extrinsic( + reveled_event, reveled_extrinsic = find_revealed_extrinsic( subtensor=subtensor, - signer_ss58=signer_keypair.ss58_address, - event_names=[ - "mevShield.DecryptedExecuted", - "mevShield.DecryptedRejected", - ], + event_names=POST_SUBMIT_MEV_EVENTS, event_hash_id=event_hash_id, start_block_hash=response.extrinsic_receipt.block_hash, blocks_ahead=blocks_for_revealed_execution, ) - if revealed_extrinsic and isinstance(revealed_extrinsic, tuple): - event_name, response.mev_extrinsic_receipt = revealed_extrinsic - - if event_name == "mevShield.DecryptedRejected": - response.success = False - response.message = "DecryptedRejected event found." - return response.from_exception( - raise_error=raise_error, - error=RuntimeError(response.message), - ) - else: - response.success = False - response.error = RuntimeError(f"DecryptedExecuted event not found.") - return response.from_exception( - raise_error=raise_error, error=response.error - ) + + post_process_mev_response( + response=response, + revealed_name=reveled_event, + revealed_extrinsic=reveled_extrinsic, + raise_error=raise_error, + ) logging.debug("[green]Encrypted extrinsic submitted successfully.[/green]") else: From f5f300eb11ca696ddcde71e6f80634dd28e38958 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Wed, 3 Dec 2025 14:52:33 -0800 Subject: [PATCH 55/67] ruff --- bittensor/core/extrinsics/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 7a7e80fc41..2db5bffe39 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -374,7 +374,9 @@ def post_process_mev_response( # when main extrinsic is successful but revealed extrinsic is not successful. if revealed_name in MEV_UNSUCCESSFUL_EVENTS: response.success = False - response.message = f"{revealed_name}: Check `mev_extrinsic_receipt` for details." + response.message = ( + f"{revealed_name}: Check `mev_extrinsic_receipt` for details." + ) response.error = RuntimeError(response.message) if response.error and raise_error: From e93ffe7d6ace83f14e4edac632654b5f674e2ee6 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Wed, 3 Dec 2025 15:49:47 -0800 Subject: [PATCH 56/67] add `BT_MEV_PROTECTION` local env variable --- bittensor/core/settings.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bittensor/core/settings.py b/bittensor/core/settings.py index 03bf071765..9b5062e1ad 100644 --- a/bittensor/core/settings.py +++ b/bittensor/core/settings.py @@ -72,7 +72,12 @@ # Default MEV Shield protection setting for extrinsics. # When enabled, transactions are encrypted to protect against Miner Extractable Value (MEV) attacks. -DEFAULT_MEV_PROTECTION = False +DEFAULT_MEV_PROTECTION = os.getenv("BT_MEV_PROTECTION", "").lower() in ( + "1", + "true", + "yes", + "on", +) # Block Explorers map network to explorer url # Must all be polkadotjs explorer urls From 799343ca8a01c4fce4b5781869076dd481586b41 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Wed, 3 Dec 2025 16:56:11 -0800 Subject: [PATCH 57/67] add `wait_for_revealed_execution` param to all extrinsics + unit tests --- bittensor/core/async_subtensor.py | 166 ++++++++++++++- bittensor/core/extrinsics/asyncex/children.py | 6 +- .../core/extrinsics/asyncex/crowdloan.py | 36 +++- .../core/extrinsics/asyncex/liquidity.py | 16 +- .../core/extrinsics/asyncex/move_stake.py | 11 +- bittensor/core/extrinsics/asyncex/proxy.py | 42 +++- .../core/extrinsics/asyncex/registration.py | 16 +- bittensor/core/extrinsics/asyncex/root.py | 12 +- bittensor/core/extrinsics/asyncex/serving.py | 10 +- bittensor/core/extrinsics/asyncex/staking.py | 10 +- .../core/extrinsics/asyncex/start_call.py | 4 +- bittensor/core/extrinsics/asyncex/take.py | 4 +- bittensor/core/extrinsics/asyncex/transfer.py | 4 +- .../core/extrinsics/asyncex/unstaking.py | 10 +- bittensor/core/extrinsics/asyncex/weights.py | 16 +- bittensor/core/extrinsics/children.py | 8 +- bittensor/core/extrinsics/crowdloan.py | 36 +++- bittensor/core/extrinsics/liquidity.py | 16 +- bittensor/core/extrinsics/move_stake.py | 13 +- bittensor/core/extrinsics/proxy.py | 42 +++- bittensor/core/extrinsics/registration.py | 16 +- bittensor/core/extrinsics/root.py | 12 +- bittensor/core/extrinsics/serving.py | 10 +- bittensor/core/extrinsics/staking.py | 11 +- bittensor/core/extrinsics/start_call.py | 4 +- bittensor/core/extrinsics/take.py | 4 +- bittensor/core/extrinsics/transfer.py | 4 +- bittensor/core/extrinsics/unstaking.py | 12 +- bittensor/core/extrinsics/weights.py | 16 +- bittensor/core/subtensor.py | 193 ++++++++++++++++-- tests/unit_tests/extrinsics/test_root.py | 1 + tests/unit_tests/extrinsics/test_serving.py | 5 + tests/unit_tests/extrinsics/test_staking.py | 1 + tests/unit_tests/extrinsics/test_unstaking.py | 3 + tests/unit_tests/test_async_subtensor.py | 34 ++- tests/unit_tests/test_subtensor.py | 71 +++++-- 36 files changed, 744 insertions(+), 131 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 2a01b78a72..88ef814d56 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5817,6 +5817,7 @@ async def add_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified @@ -5843,6 +5844,7 @@ async def add_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5866,6 +5868,7 @@ async def add_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def add_liquidity( @@ -5882,6 +5885,7 @@ async def add_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds liquidity to the specified price range. @@ -5902,6 +5906,7 @@ async def add_liquidity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5922,6 +5927,7 @@ async def add_liquidity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def add_stake_multiple( @@ -5936,6 +5942,7 @@ async def add_stake_multiple( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds stakes to multiple neurons identified by their hotkey SS58 addresses. @@ -5955,6 +5962,7 @@ async def add_stake_multiple( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5973,6 +5981,7 @@ async def add_stake_multiple( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def add_proxy( @@ -5987,6 +5996,7 @@ async def add_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds a proxy relationship. @@ -6010,6 +6020,7 @@ async def add_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6029,6 +6040,7 @@ async def add_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def announce_proxy( @@ -6042,6 +6054,7 @@ async def announce_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Announces a future call that will be executed through a proxy. @@ -6063,6 +6076,7 @@ async def announce_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6081,6 +6095,7 @@ async def announce_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def burned_register( @@ -6093,6 +6108,7 @@ async def burned_register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling @@ -6110,6 +6126,7 @@ async def burned_register( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6124,6 +6141,7 @@ async def burned_register( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) return await burned_register_extrinsic( @@ -6135,6 +6153,7 @@ async def burned_register( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def claim_root( @@ -6147,6 +6166,7 @@ async def claim_root( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ): """Claims the root emissions for a coldkey. @@ -6162,6 +6182,7 @@ async def claim_root( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6175,6 +6196,7 @@ async def claim_root( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def commit_weights( @@ -6193,6 +6215,7 @@ async def commit_weights( raise_error: bool = True, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Commits a hash of the subnet validator's weight vector to the Bittensor blockchain using the provided wallet. @@ -6216,6 +6239,7 @@ async def commit_weights( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6249,10 +6273,11 @@ async def commit_weights( weights=weights, salt=salt, mev_protection=mev_protection, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) except Exception as error: return ExtrinsicResponse.from_exception( @@ -6278,6 +6303,7 @@ async def contribute_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Contributes funds to an active crowdloan campaign. @@ -6295,6 +6321,7 @@ async def contribute_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6309,6 +6336,7 @@ async def contribute_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def create_crowdloan( @@ -6326,6 +6354,7 @@ async def create_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Creates a new crowdloan campaign on-chain. @@ -6347,6 +6376,7 @@ async def create_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6365,6 +6395,7 @@ async def create_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def create_pure_proxy( @@ -6379,6 +6410,7 @@ async def create_pure_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Creates a pure proxy account. @@ -6401,6 +6433,7 @@ async def create_pure_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6421,6 +6454,7 @@ async def create_pure_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def dissolve_crowdloan( @@ -6433,6 +6467,7 @@ async def dissolve_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Dissolves a completed or failed crowdloan campaign after all refunds are processed. @@ -6452,6 +6487,7 @@ async def dissolve_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6471,6 +6507,7 @@ async def dissolve_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def finalize_crowdloan( @@ -6483,6 +6520,7 @@ async def finalize_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Finalizes a successful crowdloan campaign once the cap has been reached and the end block has passed. @@ -6499,6 +6537,7 @@ async def finalize_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6512,6 +6551,7 @@ async def finalize_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def kill_pure_proxy( @@ -6530,6 +6570,7 @@ async def kill_pure_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Kills (removes) a pure proxy account. @@ -6567,6 +6608,7 @@ async def kill_pure_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6595,6 +6637,7 @@ async def kill_pure_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def mev_submit_encrypted( @@ -6602,6 +6645,7 @@ async def mev_submit_encrypted( wallet: "Wallet", call: "GenericCall", signer_keypair: Optional["Keypair"] = None, + *, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -6671,6 +6715,7 @@ async def modify_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Modifies liquidity in liquidity position by adding or removing liquidity from it. @@ -6689,6 +6734,7 @@ async def modify_liquidity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6734,6 +6780,7 @@ async def modify_liquidity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def move_stake( @@ -6751,6 +6798,7 @@ async def move_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Moves stake to a different hotkey and/or subnet. @@ -6772,6 +6820,7 @@ async def move_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6791,6 +6840,7 @@ async def move_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def poke_deposit( @@ -6802,6 +6852,7 @@ async def poke_deposit( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adjusts deposits made for proxies and announcements based on current values. @@ -6819,6 +6870,7 @@ async def poke_deposit( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6840,6 +6892,7 @@ async def poke_deposit( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def proxy( @@ -6854,6 +6907,7 @@ async def proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Executes a call on behalf of the real account through a proxy. @@ -6877,6 +6931,7 @@ async def proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6896,6 +6951,7 @@ async def proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def proxy_announced( @@ -6911,6 +6967,7 @@ async def proxy_announced( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Executes an announced call on behalf of the real account through a proxy. @@ -6935,6 +6992,7 @@ async def proxy_announced( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6955,6 +7013,7 @@ async def proxy_announced( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def refund_crowdloan( @@ -6967,6 +7026,7 @@ async def refund_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Refunds contributors from a failed or expired crowdloan campaign. @@ -6986,6 +7046,7 @@ async def refund_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7005,6 +7066,7 @@ async def refund_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def reject_proxy_announcement( @@ -7018,6 +7080,7 @@ async def reject_proxy_announcement( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Rejects an announcement made by a proxy delegate. @@ -7039,6 +7102,7 @@ async def reject_proxy_announcement( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7056,6 +7120,7 @@ async def reject_proxy_announcement( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def register( @@ -7076,6 +7141,7 @@ async def register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. @@ -7104,6 +7170,7 @@ async def register( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7128,6 +7195,7 @@ async def register( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def register_subnet( @@ -7139,6 +7207,7 @@ async def register_subnet( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers a new subnetwork on the Bittensor network. @@ -7154,6 +7223,7 @@ async def register_subnet( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7166,6 +7236,7 @@ async def register_subnet( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def remove_proxy_announcement( @@ -7179,6 +7250,7 @@ async def remove_proxy_announcement( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes an announcement made by a proxy account. @@ -7200,6 +7272,7 @@ async def remove_proxy_announcement( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7218,6 +7291,7 @@ async def remove_proxy_announcement( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def remove_liquidity( @@ -7232,6 +7306,7 @@ async def remove_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Remove liquidity and credit balances back to wallet's hotkey stake. @@ -7249,6 +7324,7 @@ async def remove_liquidity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7269,6 +7345,7 @@ async def remove_liquidity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def remove_proxies( @@ -7280,6 +7357,7 @@ async def remove_proxies( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes all proxy relationships for the account in a single transaction. @@ -7300,6 +7378,7 @@ async def remove_proxies( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7316,6 +7395,7 @@ async def remove_proxies( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def remove_proxy( @@ -7330,6 +7410,7 @@ async def remove_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes a specific proxy relationship. @@ -7352,6 +7433,7 @@ async def remove_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7371,6 +7453,7 @@ async def remove_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def reveal_weights( @@ -7389,6 +7472,7 @@ async def reveal_weights( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. @@ -7412,6 +7496,7 @@ async def reveal_weights( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7443,6 +7528,7 @@ async def reveal_weights( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) except Exception as error: return ExtrinsicResponse.from_exception( @@ -7463,6 +7549,7 @@ async def root_register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Register neuron by recycling some TAO. @@ -7478,6 +7565,7 @@ async def root_register( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7491,6 +7579,7 @@ async def root_register( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def root_set_pending_childkey_cooldown( @@ -7503,6 +7592,7 @@ async def root_set_pending_childkey_cooldown( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the pending childkey cooldown. @@ -7518,6 +7608,7 @@ async def root_set_pending_childkey_cooldown( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7533,6 +7624,7 @@ async def root_set_pending_childkey_cooldown( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def set_auto_stake( @@ -7546,6 +7638,7 @@ async def set_auto_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the coldkey to automatically stake to the hotkey within specific subnet mechanism. @@ -7563,6 +7656,7 @@ async def set_auto_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7580,6 +7674,7 @@ async def set_auto_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def set_children( @@ -7594,6 +7689,7 @@ async def set_children( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Allows a coldkey to set children-keys. @@ -7612,6 +7708,7 @@ async def set_children( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7640,6 +7737,7 @@ async def set_children( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def set_delegate_take( @@ -7653,6 +7751,7 @@ async def set_delegate_take( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Sets the delegate 'take' percentage for a neuron identified by its hotkey. @@ -7671,6 +7770,7 @@ async def set_delegate_take( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7712,6 +7812,7 @@ async def set_delegate_take( raise_error=raise_error, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + wait_for_revealed_execution=wait_for_revealed_execution, ) if response.success: @@ -7730,6 +7831,7 @@ async def set_root_claim_type( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ): """Sets the root claim type for the coldkey in provided wallet. @@ -7749,6 +7851,7 @@ async def set_root_claim_type( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7762,6 +7865,7 @@ async def set_root_claim_type( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def set_subnet_identity( @@ -7775,6 +7879,7 @@ async def set_subnet_identity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Sets the identity of a subnet for a specific wallet and network. @@ -7793,6 +7898,7 @@ async def set_subnet_identity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7814,6 +7920,7 @@ async def set_subnet_identity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def set_weights( @@ -7833,6 +7940,7 @@ async def set_weights( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Sets the weight vector for a neuron acting as a validator, specifying the weights assigned to subnet miners @@ -7862,6 +7970,7 @@ async def set_weights( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7923,6 +8032,7 @@ async def _blocks_weight_limit() -> bool: raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) except Exception as error: return ExtrinsicResponse.from_exception( @@ -7955,6 +8065,7 @@ async def _blocks_weight_limit() -> bool: raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) except Exception as error: return ExtrinsicResponse.from_exception( @@ -7979,6 +8090,7 @@ async def serve_axon( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. @@ -7999,6 +8111,7 @@ async def serve_axon( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8016,6 +8129,7 @@ async def serve_axon( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def set_commitment( @@ -8029,6 +8143,7 @@ async def set_commitment( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -8049,6 +8164,7 @@ async def set_commitment( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8073,6 +8189,7 @@ async def set_commitment( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def set_reveal_commitment( @@ -8082,10 +8199,13 @@ async def set_reveal_commitment( data: str, blocks_until_reveal: int = 360, block_time: Union[int, float] = 12, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -8097,12 +8217,16 @@ async def set_reveal_commitment( blocks_until_reveal: The number of blocks from now after which the data will be revealed. Then number of blocks in one epoch. block_time: The number of seconds between each block. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8123,10 +8247,12 @@ async def set_reveal_commitment( netuid=netuid, data_type="TimelockEncrypted", data=data_, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) response.data = data_ return response @@ -8141,6 +8267,7 @@ async def start_call( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start @@ -8158,6 +8285,7 @@ async def start_call( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8171,6 +8299,7 @@ async def start_call( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def swap_stake( @@ -8189,6 +8318,7 @@ async def swap_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -8216,6 +8346,7 @@ async def swap_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8243,6 +8374,7 @@ async def swap_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def toggle_user_liquidity( @@ -8256,6 +8388,7 @@ async def toggle_user_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Allow to toggle user liquidity for specified subnet. @@ -8272,6 +8405,7 @@ async def toggle_user_liquidity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8288,6 +8422,7 @@ async def toggle_user_liquidity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def transfer( @@ -8303,6 +8438,7 @@ async def transfer( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Transfer token of amount to destination. @@ -8322,6 +8458,7 @@ async def transfer( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8339,6 +8476,7 @@ async def transfer( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def transfer_stake( @@ -8355,6 +8493,7 @@ async def transfer_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -8375,6 +8514,7 @@ async def transfer_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8393,6 +8533,7 @@ async def transfer_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def unstake( @@ -8410,6 +8551,7 @@ async def unstake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting @@ -8436,6 +8578,7 @@ async def unstake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8458,6 +8601,7 @@ async def unstake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def unstake_all( @@ -8472,6 +8616,7 @@ async def unstake_all( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. @@ -8490,6 +8635,7 @@ async def unstake_all( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8544,6 +8690,7 @@ async def unstake_all( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def unstake_multiple( @@ -8559,6 +8706,7 @@ async def unstake_multiple( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts @@ -8579,6 +8727,7 @@ async def unstake_multiple( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8598,6 +8747,7 @@ async def unstake_multiple( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def update_cap_crowdloan( @@ -8611,6 +8761,7 @@ async def update_cap_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Updates the fundraising cap (maximum total contribution) of a non-finalized crowdloan. @@ -8631,6 +8782,7 @@ async def update_cap_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8650,6 +8802,7 @@ async def update_cap_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def update_end_crowdloan( @@ -8663,6 +8816,7 @@ async def update_end_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Updates the end block of a non-finalized crowdloan campaign. @@ -8683,6 +8837,7 @@ async def update_end_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8703,6 +8858,7 @@ async def update_end_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def update_min_contribution_crowdloan( @@ -8716,6 +8872,7 @@ async def update_min_contribution_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Updates the minimum contribution amount of a non-finalized crowdloan. @@ -8736,6 +8893,7 @@ async def update_min_contribution_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8755,6 +8913,7 @@ async def update_min_contribution_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) async def withdraw_crowdloan( @@ -8767,6 +8926,7 @@ async def withdraw_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Withdraws a contribution from an active (not yet finalized or dissolved) crowdloan. @@ -8783,6 +8943,7 @@ async def withdraw_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -8800,6 +8961,7 @@ async def withdraw_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) diff --git a/bittensor/core/extrinsics/asyncex/children.py b/bittensor/core/extrinsics/asyncex/children.py index c51d807cd4..49d2852254 100644 --- a/bittensor/core/extrinsics/asyncex/children.py +++ b/bittensor/core/extrinsics/asyncex/children.py @@ -23,6 +23,7 @@ async def set_children_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Allows a coldkey to set children-keys. @@ -83,7 +84,7 @@ async def set_children_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -110,6 +111,7 @@ async def root_set_pending_childkey_cooldown_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Allows a root coldkey to set children-keys. @@ -152,7 +154,7 @@ async def root_set_pending_childkey_cooldown_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/crowdloan.py b/bittensor/core/extrinsics/asyncex/crowdloan.py index 3f5de9a3a0..05bbd8c086 100644 --- a/bittensor/core/extrinsics/asyncex/crowdloan.py +++ b/bittensor/core/extrinsics/asyncex/crowdloan.py @@ -24,6 +24,7 @@ async def contribute_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Contributes funds to an active crowdloan campaign. @@ -42,6 +43,7 @@ async def contribute_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -68,7 +70,7 @@ async def contribute_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -99,6 +101,7 @@ async def create_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Creates a new crowdloan campaign on-chain. @@ -121,6 +124,7 @@ async def create_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -153,7 +157,7 @@ async def create_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -179,6 +183,7 @@ async def dissolve_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Dissolves a completed or failed crowdloan campaign after all refunds are processed. @@ -199,6 +204,7 @@ async def dissolve_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -226,7 +232,7 @@ async def dissolve_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -252,6 +258,7 @@ async def finalize_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Finalizes a successful crowdloan campaign once the cap has been reached and the end block has passed. @@ -269,6 +276,7 @@ async def finalize_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -290,7 +298,7 @@ async def finalize_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -316,6 +324,7 @@ async def refund_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Refunds contributors from a failed or expired crowdloan campaign. @@ -336,6 +345,7 @@ async def refund_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -363,7 +373,7 @@ async def refund_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -390,6 +400,7 @@ async def update_cap_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Updates the fundraising cap (maximum total contribution) of a non-finalized crowdloan. @@ -411,6 +422,7 @@ async def update_cap_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -441,7 +453,7 @@ async def update_cap_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -468,6 +480,7 @@ async def update_end_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Updates the end block of a non-finalized crowdloan campaign. @@ -489,6 +502,7 @@ async def update_end_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -518,7 +532,7 @@ async def update_end_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -545,6 +559,7 @@ async def update_min_contribution_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Updates the minimum contribution amount of a non-finalized crowdloan. @@ -566,6 +581,7 @@ async def update_min_contribution_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -596,7 +612,7 @@ async def update_min_contribution_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -622,6 +638,7 @@ async def withdraw_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Withdraws a contribution from an active (not yet finalized or dissolved) crowdloan. @@ -639,6 +656,7 @@ async def withdraw_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -664,7 +682,7 @@ async def withdraw_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/liquidity.py b/bittensor/core/extrinsics/asyncex/liquidity.py index fdae17a441..aa828f2c1a 100644 --- a/bittensor/core/extrinsics/asyncex/liquidity.py +++ b/bittensor/core/extrinsics/asyncex/liquidity.py @@ -26,6 +26,7 @@ async def add_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds liquidity to the specified price range. @@ -47,6 +48,7 @@ async def add_liquidity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -80,7 +82,7 @@ async def add_liquidity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -108,6 +110,7 @@ async def modify_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Modifies liquidity in liquidity position by adding or removing liquidity from it. @@ -127,6 +130,7 @@ async def modify_liquidity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -159,7 +163,7 @@ async def modify_liquidity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -186,6 +190,7 @@ async def remove_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Remove liquidity and credit balances back to wallet's hotkey stake. @@ -204,6 +209,7 @@ async def remove_liquidity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -235,7 +241,7 @@ async def remove_liquidity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -261,6 +267,7 @@ async def toggle_user_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Allow to toggle user liquidity for specified subnet. @@ -278,6 +285,7 @@ async def toggle_user_liquidity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -302,7 +310,7 @@ async def toggle_user_liquidity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index fcd681798c..25f7b2db6f 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -56,6 +56,7 @@ async def move_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Moves stake from one hotkey to another within subnets in the Bittensor network. @@ -78,6 +79,7 @@ async def move_stake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -137,7 +139,7 @@ async def move_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -210,6 +212,7 @@ async def transfer_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Transfers stake from one coldkey to another in the Bittensor network. @@ -231,6 +234,7 @@ async def transfer_stake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -284,7 +288,7 @@ async def transfer_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -351,6 +355,7 @@ async def swap_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Swaps stake from one subnet to another for a given hotkey in the Bittensor network. @@ -452,7 +457,7 @@ async def swap_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/proxy.py b/bittensor/core/extrinsics/asyncex/proxy.py index 749934b62d..058e272fb4 100644 --- a/bittensor/core/extrinsics/asyncex/proxy.py +++ b/bittensor/core/extrinsics/asyncex/proxy.py @@ -28,6 +28,7 @@ async def add_proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds a proxy relationship. @@ -48,6 +49,7 @@ async def add_proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -88,7 +90,7 @@ async def add_proxy_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -123,6 +125,7 @@ async def remove_proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes a proxy relationship. @@ -142,6 +145,7 @@ async def remove_proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -175,7 +179,7 @@ async def remove_proxy_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -207,6 +211,7 @@ async def remove_proxies_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes all proxy relationships for the account. @@ -224,6 +229,7 @@ async def remove_proxies_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -250,7 +256,7 @@ async def remove_proxies_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -285,6 +291,7 @@ async def create_pure_proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Creates a pure proxy account. @@ -304,6 +311,7 @@ async def create_pure_proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -337,7 +345,7 @@ async def create_pure_proxy_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -401,6 +409,7 @@ async def kill_pure_proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Kills (removes) a pure proxy account. @@ -438,6 +447,7 @@ async def kill_pure_proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -544,6 +554,7 @@ async def proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Executes a call on behalf of the real account through a proxy. @@ -564,6 +575,7 @@ async def proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -601,7 +613,7 @@ async def proxy_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -637,6 +649,7 @@ async def proxy_announced_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Executes an announced call on behalf of the real account through a proxy. @@ -661,6 +674,7 @@ async def proxy_announced_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -699,7 +713,7 @@ async def proxy_announced_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -733,6 +747,7 @@ async def announce_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Announces a future call that will be executed through a proxy. @@ -751,6 +766,7 @@ async def announce_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -784,7 +800,7 @@ async def announce_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -818,6 +834,7 @@ async def reject_announcement_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Rejects an announcement made by a proxy delegate. @@ -839,6 +856,7 @@ async def reject_announcement_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -872,7 +890,7 @@ async def reject_announcement_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -906,6 +924,7 @@ async def remove_announcement_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes an announcement made by a proxy account. @@ -927,6 +946,7 @@ async def remove_announcement_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -960,7 +980,7 @@ async def remove_announcement_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -992,6 +1012,7 @@ async def poke_deposit_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adjusts deposits made for proxies and announcements based on current values. @@ -1010,6 +1031,7 @@ async def poke_deposit_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -1041,7 +1063,7 @@ async def poke_deposit_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 8a16b30c7f..adcd78c992 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -28,6 +28,7 @@ async def burned_register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Registers the wallet to chain by recycling TAO. @@ -44,6 +45,7 @@ async def burned_register_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -100,7 +102,7 @@ async def burned_register_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -165,6 +167,7 @@ async def register_subnet_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers a new subnetwork on the Bittensor blockchain asynchronously. @@ -181,6 +184,7 @@ async def register_subnet_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -215,7 +219,7 @@ async def register_subnet_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -259,6 +263,7 @@ async def register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. @@ -286,6 +291,7 @@ async def register_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -393,7 +399,7 @@ async def register_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -463,6 +469,7 @@ async def set_subnet_identity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Set the identity information for a given subnet. @@ -488,6 +495,7 @@ async def set_subnet_identity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -521,7 +529,7 @@ async def set_subnet_identity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index a1fbb6cc08..782569e983 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -49,6 +49,7 @@ async def root_register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers the neuron to the root network. @@ -65,6 +66,7 @@ async def root_register_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -125,7 +127,7 @@ async def root_register_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -174,6 +176,7 @@ async def set_root_claim_type_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the root claim type for the coldkey in provided wallet. @@ -194,6 +197,7 @@ async def set_root_claim_type_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -219,7 +223,7 @@ async def set_root_claim_type_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -245,6 +249,7 @@ async def claim_root_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Claims the root emissions for a coldkey. @@ -261,6 +266,7 @@ async def claim_root_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -282,7 +288,7 @@ async def claim_root_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index c33b461971..e504e511b4 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -34,6 +34,7 @@ async def serve_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Subscribes a Bittensor endpoint to the subtensor chain. @@ -57,6 +58,7 @@ async def serve_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -115,7 +117,7 @@ async def serve_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -153,6 +155,7 @@ async def serve_axon_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Serves the axon to the network. @@ -171,6 +174,7 @@ async def serve_axon_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -235,6 +239,7 @@ async def publish_metadata_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. @@ -258,6 +263,7 @@ async def publish_metadata_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -293,7 +299,7 @@ async def publish_metadata_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index 8a4640dc2f..69d6236199 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -33,6 +33,7 @@ async def add_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. @@ -57,6 +58,7 @@ async def add_stake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -148,7 +150,7 @@ async def add_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -224,6 +226,7 @@ async def add_stake_multiple_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey on subnet with @@ -244,6 +247,7 @@ async def add_stake_multiple_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -451,6 +455,7 @@ async def set_auto_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the coldkey to automatically stake to the hotkey within specific subnet mechanism. @@ -469,6 +474,7 @@ async def set_auto_stake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -492,7 +498,7 @@ async def set_auto_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/start_call.py b/bittensor/core/extrinsics/asyncex/start_call.py index 7b27bacd11..b0e71eb93e 100644 --- a/bittensor/core/extrinsics/asyncex/start_call.py +++ b/bittensor/core/extrinsics/asyncex/start_call.py @@ -20,6 +20,7 @@ async def start_call_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -38,6 +39,7 @@ async def start_call_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -59,7 +61,7 @@ async def start_call_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/take.py b/bittensor/core/extrinsics/asyncex/take.py index 56d6a42dfc..78c4d02089 100644 --- a/bittensor/core/extrinsics/asyncex/take.py +++ b/bittensor/core/extrinsics/asyncex/take.py @@ -23,6 +23,7 @@ async def set_take_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the delegate 'take' percentage for a neuron identified by its hotkey. @@ -41,6 +42,7 @@ async def set_take_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -71,7 +73,7 @@ async def set_take_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/transfer.py b/bittensor/core/extrinsics/asyncex/transfer.py index 83365ea0f4..9bde182535 100644 --- a/bittensor/core/extrinsics/asyncex/transfer.py +++ b/bittensor/core/extrinsics/asyncex/transfer.py @@ -35,6 +35,7 @@ async def transfer_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Transfers funds from this wallet to the destination public key address. @@ -54,6 +55,7 @@ async def transfer_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -125,7 +127,7 @@ async def transfer_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 20abc7583a..9ea41f9352 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -34,6 +34,7 @@ async def unstake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes stake into the wallet coldkey from the specified hotkey ``uid``. @@ -56,6 +57,7 @@ async def unstake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -137,7 +139,7 @@ async def unstake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -214,6 +216,7 @@ async def unstake_all_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. @@ -233,6 +236,7 @@ async def unstake_all_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -261,7 +265,7 @@ async def unstake_all_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return await subtensor.sign_and_send_extrinsic( @@ -293,6 +297,7 @@ async def unstake_multiple_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. @@ -314,6 +319,7 @@ async def unstake_multiple_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index eba2259ffd..7aa2ad63e0 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -36,6 +36,7 @@ async def commit_timelocked_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Commits the weights for a specific sub subnet mechanism on the Bittensor blockchain using the provided wallet. @@ -58,6 +59,7 @@ async def commit_timelocked_weights_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -114,7 +116,7 @@ async def commit_timelocked_weights_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -159,6 +161,7 @@ async def commit_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Commits the weights for a specific sub subnet on the Bittensor blockchain using the provided wallet. @@ -180,6 +183,7 @@ async def commit_weights_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -220,7 +224,7 @@ async def commit_weights_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -261,6 +265,7 @@ async def reveal_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Reveals the weights for a specific sub subnet on the Bittensor blockchain using the provided wallet. @@ -283,6 +288,7 @@ async def reveal_weights_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -318,7 +324,7 @@ async def reveal_weights_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( @@ -358,6 +364,7 @@ async def set_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Sets the passed weights in the chain for hotkeys in the sub-subnet of the passed subnet. @@ -379,6 +386,7 @@ async def set_weights_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -413,7 +421,7 @@ async def set_weights_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = await subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/children.py b/bittensor/core/extrinsics/children.py index 5aac2f4ef3..5172d2c2f2 100644 --- a/bittensor/core/extrinsics/children.py +++ b/bittensor/core/extrinsics/children.py @@ -23,6 +23,7 @@ def set_children_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Allows a coldkey to set children-keys. @@ -42,6 +43,7 @@ def set_children_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -83,7 +85,7 @@ def set_children_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -110,6 +112,7 @@ def root_set_pending_childkey_cooldown_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Allows a root coldkey to set children-keys. @@ -127,6 +130,7 @@ def root_set_pending_childkey_cooldown_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -152,7 +156,7 @@ def root_set_pending_childkey_cooldown_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/crowdloan.py b/bittensor/core/extrinsics/crowdloan.py index 81d721ab79..2acfda7e08 100644 --- a/bittensor/core/extrinsics/crowdloan.py +++ b/bittensor/core/extrinsics/crowdloan.py @@ -24,6 +24,7 @@ def contribute_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Contributes funds to an active crowdloan campaign. @@ -42,6 +43,7 @@ def contribute_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -67,7 +69,7 @@ def contribute_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -98,6 +100,7 @@ def create_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Creates a new crowdloan campaign on-chain. @@ -120,6 +123,7 @@ def create_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -152,7 +156,7 @@ def create_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -178,6 +182,7 @@ def dissolve_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Dissolves a completed or failed crowdloan campaign after all refunds are processed. @@ -198,6 +203,7 @@ def dissolve_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -225,7 +231,7 @@ def dissolve_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -251,6 +257,7 @@ def finalize_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Finalizes a successful crowdloan campaign once the cap has been reached and the end block has passed. @@ -268,6 +275,7 @@ def finalize_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -289,7 +297,7 @@ def finalize_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -315,6 +323,7 @@ def refund_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Refunds contributors from a failed or expired crowdloan campaign. @@ -335,6 +344,7 @@ def refund_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -362,7 +372,7 @@ def refund_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -389,6 +399,7 @@ def update_cap_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Updates the fundraising cap (maximum total contribution) of a non-finalized crowdloan. @@ -410,6 +421,7 @@ def update_cap_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -440,7 +452,7 @@ def update_cap_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -467,6 +479,7 @@ def update_end_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Updates the end block of a non-finalized crowdloan campaign. @@ -488,6 +501,7 @@ def update_end_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -517,7 +531,7 @@ def update_end_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -544,6 +558,7 @@ def update_min_contribution_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Updates the minimum contribution amount of a non-finalized crowdloan. @@ -565,6 +580,7 @@ def update_min_contribution_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -595,7 +611,7 @@ def update_min_contribution_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -621,6 +637,7 @@ def withdraw_crowdloan_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> "ExtrinsicResponse": """ Withdraws a contribution from an active (not yet finalized or dissolved) crowdloan. @@ -638,6 +655,7 @@ def withdraw_crowdloan_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -663,7 +681,7 @@ def withdraw_crowdloan_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/liquidity.py b/bittensor/core/extrinsics/liquidity.py index fbc73588b8..3dc8632339 100644 --- a/bittensor/core/extrinsics/liquidity.py +++ b/bittensor/core/extrinsics/liquidity.py @@ -26,6 +26,7 @@ def add_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds liquidity to the specified price range. @@ -47,6 +48,7 @@ def add_liquidity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -80,7 +82,7 @@ def add_liquidity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -108,6 +110,7 @@ def modify_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Modifies liquidity in liquidity position by adding or removing liquidity from it. @@ -127,6 +130,7 @@ def modify_liquidity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -159,7 +163,7 @@ def modify_liquidity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -186,6 +190,7 @@ def remove_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Remove liquidity and credit balances back to wallet's hotkey stake. @@ -204,6 +209,7 @@ def remove_liquidity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -235,7 +241,7 @@ def remove_liquidity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -261,6 +267,7 @@ def toggle_user_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Allow to toggle user liquidity for specified subnet. @@ -278,6 +285,7 @@ def toggle_user_liquidity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -302,7 +310,7 @@ def toggle_user_liquidity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index 0f134a9a9b..17ddcdbd73 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -53,6 +53,7 @@ def move_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Moves stake to a different hotkey and/or subnet while keeping the same coldkey owner. @@ -75,6 +76,7 @@ def move_stake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -133,7 +135,7 @@ def move_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -206,6 +208,7 @@ def transfer_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -227,6 +230,7 @@ def transfer_stake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -280,7 +284,7 @@ def transfer_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -347,6 +351,7 @@ def swap_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -370,7 +375,7 @@ def swap_stake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. - + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -447,7 +452,7 @@ def swap_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/proxy.py b/bittensor/core/extrinsics/proxy.py index 8d4307d6b4..508131d6a5 100644 --- a/bittensor/core/extrinsics/proxy.py +++ b/bittensor/core/extrinsics/proxy.py @@ -26,6 +26,7 @@ def add_proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds a proxy relationship. @@ -46,6 +47,7 @@ def add_proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -79,7 +81,7 @@ def add_proxy_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -114,6 +116,7 @@ def remove_proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes a proxy relationship. @@ -133,6 +136,7 @@ def remove_proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -166,7 +170,7 @@ def remove_proxy_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -198,6 +202,7 @@ def remove_proxies_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes all proxy relationships for the account. @@ -215,6 +220,7 @@ def remove_proxies_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -241,7 +247,7 @@ def remove_proxies_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -276,6 +282,7 @@ def create_pure_proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Creates a pure proxy account. @@ -295,6 +302,7 @@ def create_pure_proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -335,7 +343,7 @@ def create_pure_proxy_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -397,6 +405,7 @@ def kill_pure_proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Kills (removes) a pure proxy account. @@ -434,6 +443,7 @@ def kill_pure_proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -540,6 +550,7 @@ def proxy_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Executes a call on behalf of the real account through a proxy. @@ -560,6 +571,7 @@ def proxy_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -597,7 +609,7 @@ def proxy_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -633,6 +645,7 @@ def proxy_announced_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Executes an announced call on behalf of the real account through a proxy. @@ -657,6 +670,7 @@ def proxy_announced_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -695,7 +709,7 @@ def proxy_announced_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -729,6 +743,7 @@ def announce_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Announces a future call that will be executed through a proxy. @@ -747,6 +762,7 @@ def announce_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -780,7 +796,7 @@ def announce_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -814,6 +830,7 @@ def reject_announcement_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Rejects an announcement made by a proxy delegate. @@ -835,6 +852,7 @@ def reject_announcement_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -868,7 +886,7 @@ def reject_announcement_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -902,6 +920,7 @@ def remove_announcement_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes an announcement made by a proxy account. @@ -923,6 +942,7 @@ def remove_announcement_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -956,7 +976,7 @@ def remove_announcement_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -988,6 +1008,7 @@ def poke_deposit_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adjusts deposits made for proxies and announcements based on current values. @@ -1006,6 +1027,7 @@ def poke_deposit_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -1037,7 +1059,7 @@ def poke_deposit_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index bad3b70eb4..50962fb9c1 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -28,6 +28,7 @@ def burned_register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Registers the wallet to chain by recycling TAO. @@ -44,6 +45,7 @@ def burned_register_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -97,7 +99,7 @@ def burned_register_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -160,6 +162,7 @@ def register_subnet_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers a new subnetwork on the Bittensor blockchain. @@ -176,6 +179,7 @@ def register_subnet_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -210,7 +214,7 @@ def register_subnet_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -254,6 +258,7 @@ def register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. @@ -278,6 +283,7 @@ def register_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -386,7 +392,7 @@ def register_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -456,6 +462,7 @@ def set_subnet_identity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Set the identity information for a given subnet. @@ -481,6 +488,7 @@ def set_subnet_identity_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -514,7 +522,7 @@ def set_subnet_identity_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index bd032847a7..996136b23f 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -46,6 +46,7 @@ def root_register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers the neuron to the root network. @@ -62,6 +63,7 @@ def root_register_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -120,7 +122,7 @@ def root_register_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -169,6 +171,7 @@ def set_root_claim_type_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the root claim type for the coldkey in provided wallet. @@ -189,6 +192,7 @@ def set_root_claim_type_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -214,7 +218,7 @@ def set_root_claim_type_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -240,6 +244,7 @@ def claim_root_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Claims the root emissions for a coldkey. @@ -256,6 +261,7 @@ def claim_root_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -277,7 +283,7 @@ def claim_root_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index 5dd17c66bc..7ecdafbf01 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -33,6 +33,7 @@ def serve_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Subscribes a Bittensor endpoint to the subtensor chain. @@ -56,6 +57,7 @@ def serve_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -112,7 +114,7 @@ def serve_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -150,6 +152,7 @@ def serve_axon_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Serves the axon to the network. @@ -168,6 +171,7 @@ def serve_axon_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -230,6 +234,7 @@ def publish_metadata_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. @@ -253,6 +258,7 @@ def publish_metadata_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -286,7 +292,7 @@ def publish_metadata_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index bd38ace97e..e53e04aa2a 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -32,6 +32,7 @@ def add_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. @@ -56,6 +57,7 @@ def add_stake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -145,7 +147,7 @@ def add_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -219,6 +221,7 @@ def add_stake_multiple_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey on subnet with @@ -239,6 +242,7 @@ def add_stake_multiple_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -358,6 +362,7 @@ def add_stake_multiple_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) data.update({(idx, hotkey_ss58, netuid): response}) @@ -442,6 +447,7 @@ def set_auto_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the coldkey to automatically stake to the hotkey within specific subnet mechanism. @@ -460,6 +466,7 @@ def set_auto_stake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -483,7 +490,7 @@ def set_auto_stake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/start_call.py b/bittensor/core/extrinsics/start_call.py index 345ef5eeae..9872ce2de2 100644 --- a/bittensor/core/extrinsics/start_call.py +++ b/bittensor/core/extrinsics/start_call.py @@ -20,6 +20,7 @@ def start_call_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -38,6 +39,7 @@ def start_call_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -59,7 +61,7 @@ def start_call_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index 5b59353d17..61484d305c 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -23,6 +23,7 @@ def set_take_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the delegate 'take' percentage for a neuron identified by its hotkey. @@ -41,6 +42,7 @@ def set_take_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -71,7 +73,7 @@ def set_take_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index ab796586c9..a1f6a5469f 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -34,6 +34,7 @@ def transfer_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Transfers funds from this wallet to the destination public key address. @@ -53,6 +54,7 @@ def transfer_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -120,7 +122,7 @@ def transfer_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index 3088b8b061..0cd13f7e68 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -32,6 +32,7 @@ def unstake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes stake into the wallet coldkey from the specified hotkey ``uid``. @@ -54,6 +55,7 @@ def unstake_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -134,7 +136,7 @@ def unstake_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -209,6 +211,7 @@ def unstake_all_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. @@ -228,6 +231,7 @@ def unstake_all_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -256,7 +260,7 @@ def unstake_all_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: return subtensor.sign_and_send_extrinsic( @@ -288,6 +292,7 @@ def unstake_multiple_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. @@ -309,6 +314,7 @@ def unstake_multiple_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -439,6 +445,7 @@ def unstake_multiple_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = unstake_extrinsic( @@ -452,6 +459,7 @@ def unstake_multiple_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) data.update({(idx, hotkey_ss58, netuid): response}) diff --git a/bittensor/core/extrinsics/weights.py b/bittensor/core/extrinsics/weights.py index cb84ed69c8..155d3cac53 100644 --- a/bittensor/core/extrinsics/weights.py +++ b/bittensor/core/extrinsics/weights.py @@ -36,6 +36,7 @@ def commit_timelocked_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Commits the weights for a specific sub subnet mechanism on the Bittensor blockchain using the provided wallet. @@ -59,6 +60,7 @@ def commit_timelocked_weights_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -114,7 +116,7 @@ def commit_timelocked_weights_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -159,6 +161,7 @@ def commit_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Commits the weights for a specific sub subnet on the Bittensor blockchain using the provided wallet. @@ -180,6 +183,7 @@ def commit_weights_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -219,7 +223,7 @@ def commit_weights_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -260,6 +264,7 @@ def reveal_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Reveals the weights for a specific sub subnet on the Bittensor blockchain using the provided wallet. @@ -282,6 +287,7 @@ def reveal_weights_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -316,7 +322,7 @@ def reveal_weights_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( @@ -356,6 +362,7 @@ def set_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Sets the passed weights in the chain for hotkeys in the sub-subnet of the passed subnet. @@ -377,6 +384,7 @@ def set_weights_extrinsic( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -410,7 +418,7 @@ def set_weights_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - wait_for_revealed_execution=True, + wait_for_revealed_execution=wait_for_revealed_execution, ) else: response = subtensor.sign_and_send_extrinsic( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 92646c7b21..b49ef48eac 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1,7 +1,7 @@ import copy from datetime import datetime, timezone from functools import lru_cache -from typing import TYPE_CHECKING, Any, Iterable, Optional, Union, cast, Literal +from typing import TYPE_CHECKING, Any, Iterable, Literal, Optional, Union, cast import scalecodec from async_substrate_interface.errors import SubstrateRequestException @@ -14,8 +14,8 @@ from bittensor.core.axon import Axon from bittensor.core.chain_data import ( - CrowdloanInfo, CrowdloanConstants, + CrowdloanInfo, DelegatedInfo, DelegateInfo, DynamicInfo, @@ -24,16 +24,16 @@ NeuronInfoLite, ProposalVoteData, ProxyAnnouncementInfo, - ProxyInfo, ProxyConstants, + ProxyInfo, ProxyType, RootClaimType, SelectiveMetagraphIndex, SimSwapResult, StakeInfo, - SubnetInfo, - SubnetIdentity, SubnetHyperparameters, + SubnetIdentity, + SubnetInfo, WeightCommitInfo, decode_account_id, ) @@ -47,8 +47,8 @@ from bittensor.core.config import Config from bittensor.core.errors import ChainError from bittensor.core.extrinsics.children import ( - set_children_extrinsic, root_set_pending_childkey_cooldown_extrinsic, + set_children_extrinsic, ) from bittensor.core.extrinsics.crowdloan import ( contribute_crowdloan_extrinsic, @@ -67,10 +67,11 @@ remove_liquidity_extrinsic, toggle_user_liquidity_extrinsic, ) +from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.move_stake import ( - transfer_stake_extrinsic, - swap_stake_extrinsic, move_stake_extrinsic, + swap_stake_extrinsic, + transfer_stake_extrinsic, ) from bittensor.core.extrinsics.proxy import ( add_proxy_extrinsic, @@ -82,8 +83,8 @@ proxy_extrinsic, reject_announcement_extrinsic, remove_announcement_extrinsic, - remove_proxy_extrinsic, remove_proxies_extrinsic, + remove_proxy_extrinsic, ) from bittensor.core.extrinsics.registration import ( burned_register_extrinsic, @@ -114,7 +115,6 @@ unstake_multiple_extrinsic, ) from bittensor.core.extrinsics.utils import get_transfer_fn_params -from bittensor.core.extrinsics.mev_shield import submit_encrypted_extrinsic from bittensor.core.extrinsics.weights import ( commit_timelocked_weights_extrinsic, commit_weights_extrinsic, @@ -123,11 +123,11 @@ ) from bittensor.core.metagraph import Metagraph from bittensor.core.settings import ( - version_as_int, DEFAULT_MEV_PROTECTION, DEFAULT_PERIOD, TAO_APP_BLOCK_EXPLORER, TYPE_REGISTRY, + version_as_int, ) from bittensor.core.types import ( BlockInfo, @@ -150,22 +150,22 @@ ) from bittensor.utils.balance import ( Balance, - fixed_to_float, FixedPoint, check_balance_amount, + fixed_to_float, ) from bittensor.utils.btlogging import logging from bittensor.utils.liquidity import ( + LiquidityPosition, calculate_fees, get_fees, - tick_to_price, price_to_tick, - LiquidityPosition, + tick_to_price, ) if TYPE_CHECKING: - from bittensor_wallet import Keypair, Wallet from async_substrate_interface.sync_substrate import QueryMapResult + from bittensor_wallet import Keypair, Wallet from scalecodec.types import GenericCall @@ -4540,6 +4540,7 @@ def add_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified @@ -4566,6 +4567,8 @@ def add_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection + used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -4589,6 +4592,7 @@ def add_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def add_liquidity( @@ -4605,6 +4609,7 @@ def add_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds liquidity to the specified price range. @@ -4625,6 +4630,7 @@ def add_liquidity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -4645,6 +4651,7 @@ def add_liquidity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def add_stake_multiple( @@ -4659,6 +4666,7 @@ def add_stake_multiple( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds stakes to multiple neurons identified by their hotkey SS58 addresses. @@ -4678,6 +4686,7 @@ def add_stake_multiple( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -4696,6 +4705,7 @@ def add_stake_multiple( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def add_proxy( @@ -4710,6 +4720,7 @@ def add_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adds a proxy relationship. @@ -4733,6 +4744,7 @@ def add_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -4752,6 +4764,7 @@ def add_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def announce_proxy( @@ -4765,6 +4778,7 @@ def announce_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Announces a future call that will be executed through a proxy. @@ -4786,6 +4800,7 @@ def announce_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -4804,6 +4819,7 @@ def announce_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def burned_register( @@ -4816,6 +4832,7 @@ def burned_register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling @@ -4833,6 +4850,7 @@ def burned_register( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -4847,6 +4865,7 @@ def burned_register( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) return burned_register_extrinsic( @@ -4858,6 +4877,7 @@ def burned_register( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def claim_root( @@ -4870,6 +4890,7 @@ def claim_root( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ): """Claims the root emissions for a coldkey. @@ -4885,6 +4906,7 @@ def claim_root( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -4898,6 +4920,7 @@ def claim_root( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def commit_weights( @@ -4916,6 +4939,7 @@ def commit_weights( raise_error: bool = True, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. @@ -4939,6 +4963,7 @@ def commit_weights( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -4973,6 +4998,7 @@ def commit_weights( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) except Exception as error: return ExtrinsicResponse.from_exception( @@ -4998,6 +5024,7 @@ def contribute_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Contributes funds to an active crowdloan campaign. @@ -5015,6 +5042,7 @@ def contribute_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5029,6 +5057,7 @@ def contribute_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def create_crowdloan( @@ -5046,6 +5075,7 @@ def create_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Creates a new crowdloan campaign on-chain. @@ -5067,6 +5097,7 @@ def create_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5085,6 +5116,7 @@ def create_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def create_pure_proxy( @@ -5099,6 +5131,7 @@ def create_pure_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Creates a pure proxy account. @@ -5121,6 +5154,7 @@ def create_pure_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5141,6 +5175,7 @@ def create_pure_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def dissolve_crowdloan( @@ -5153,6 +5188,7 @@ def dissolve_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Dissolves a completed or failed crowdloan campaign after all refunds are processed. @@ -5172,6 +5208,7 @@ def dissolve_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5191,6 +5228,7 @@ def dissolve_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def finalize_crowdloan( @@ -5203,6 +5241,7 @@ def finalize_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Finalizes a successful crowdloan campaign once the cap has been reached and the end block has passed. @@ -5219,6 +5258,7 @@ def finalize_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5232,6 +5272,7 @@ def finalize_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def kill_pure_proxy( @@ -5250,6 +5291,7 @@ def kill_pure_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Kills (removes) a pure proxy account. @@ -5287,6 +5329,7 @@ def kill_pure_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5315,6 +5358,7 @@ def kill_pure_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def mev_submit_encrypted( @@ -5322,6 +5366,7 @@ def mev_submit_encrypted( wallet: "Wallet", call: "GenericCall", signer_keypair: Optional["Keypair"] = None, + *, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -5391,6 +5436,7 @@ def modify_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Modifies liquidity in liquidity position by adding or removing liquidity from it. @@ -5409,6 +5455,7 @@ def modify_liquidity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5454,6 +5501,7 @@ def modify_liquidity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def move_stake( @@ -5471,6 +5519,7 @@ def move_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Moves stake to a different hotkey and/or subnet. @@ -5492,6 +5541,7 @@ def move_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5511,6 +5561,7 @@ def move_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def poke_deposit( @@ -5522,6 +5573,7 @@ def poke_deposit( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Adjusts deposits made for proxies and announcements based on current values. @@ -5539,6 +5591,7 @@ def poke_deposit( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5560,6 +5613,7 @@ def poke_deposit( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def proxy( @@ -5574,6 +5628,7 @@ def proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Executes a call on behalf of the real account through a proxy. @@ -5597,6 +5652,7 @@ def proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5616,6 +5672,7 @@ def proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def proxy_announced( @@ -5631,6 +5688,7 @@ def proxy_announced( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Executes an announced call on behalf of the real account through a proxy. @@ -5655,6 +5713,7 @@ def proxy_announced( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5675,6 +5734,7 @@ def proxy_announced( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def refund_crowdloan( @@ -5687,6 +5747,7 @@ def refund_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Refunds contributors from a failed or expired crowdloan campaign. @@ -5706,6 +5767,7 @@ def refund_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5725,6 +5787,7 @@ def refund_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def reject_proxy_announcement( @@ -5738,6 +5801,7 @@ def reject_proxy_announcement( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Rejects an announcement made by a proxy delegate. @@ -5759,6 +5823,7 @@ def reject_proxy_announcement( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5776,6 +5841,7 @@ def reject_proxy_announcement( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def register( @@ -5796,6 +5862,7 @@ def register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. @@ -5824,6 +5891,7 @@ def register( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5848,6 +5916,7 @@ def register( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def register_subnet( @@ -5859,6 +5928,7 @@ def register_subnet( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers a new subnetwork on the Bittensor network. @@ -5874,6 +5944,7 @@ def register_subnet( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5886,6 +5957,7 @@ def register_subnet( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def remove_proxy_announcement( @@ -5899,6 +5971,7 @@ def remove_proxy_announcement( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes an announcement made by a proxy account. @@ -5920,6 +5993,7 @@ def remove_proxy_announcement( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5938,6 +6012,7 @@ def remove_proxy_announcement( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def remove_liquidity( @@ -5952,6 +6027,7 @@ def remove_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Remove liquidity and credit balances back to wallet's hotkey stake. @@ -5969,6 +6045,7 @@ def remove_liquidity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -5989,6 +6066,7 @@ def remove_liquidity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def remove_proxies( @@ -6000,6 +6078,7 @@ def remove_proxies( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes all proxy relationships for the account in a single transaction. @@ -6020,6 +6099,7 @@ def remove_proxies( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6036,6 +6116,7 @@ def remove_proxies( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def remove_proxy( @@ -6050,6 +6131,7 @@ def remove_proxy( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes a specific proxy relationship. @@ -6072,6 +6154,7 @@ def remove_proxy( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6091,6 +6174,7 @@ def remove_proxy( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def reveal_weights( @@ -6109,6 +6193,7 @@ def reveal_weights( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. @@ -6132,6 +6217,7 @@ def reveal_weights( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6163,6 +6249,7 @@ def reveal_weights( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) except Exception as error: return ExtrinsicResponse.from_exception( @@ -6183,6 +6270,7 @@ def root_register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Register neuron by recycling some TAO. @@ -6198,6 +6286,7 @@ def root_register( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6211,6 +6300,7 @@ def root_register( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def root_set_pending_childkey_cooldown( @@ -6223,6 +6313,7 @@ def root_set_pending_childkey_cooldown( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the pending childkey cooldown. @@ -6238,6 +6329,7 @@ def root_set_pending_childkey_cooldown( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6253,6 +6345,7 @@ def root_set_pending_childkey_cooldown( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def set_auto_stake( @@ -6266,6 +6359,7 @@ def set_auto_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Sets the coldkey to automatically stake to the hotkey within specific subnet mechanism. @@ -6283,6 +6377,7 @@ def set_auto_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6300,6 +6395,7 @@ def set_auto_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def set_children( @@ -6314,6 +6410,7 @@ def set_children( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Allows a coldkey to set children-keys. @@ -6332,6 +6429,7 @@ def set_children( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6347,6 +6445,7 @@ def set_children( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def set_delegate_take( @@ -6358,6 +6457,7 @@ def set_delegate_take( wait_for_finalization: bool = True, raise_error: bool = False, period: Optional[int] = DEFAULT_PERIOD, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Sets the delegate 'take' percentage for a neuron identified by its hotkey. @@ -6373,6 +6473,7 @@ def set_delegate_take( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6413,6 +6514,7 @@ def set_delegate_take( raise_error=raise_error, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + wait_for_revealed_execution=wait_for_revealed_execution, ) if response.success: @@ -6431,6 +6533,7 @@ def set_root_claim_type( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ): """Sets the root claim type for the coldkey in provided wallet. @@ -6450,6 +6553,7 @@ def set_root_claim_type( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6463,6 +6567,7 @@ def set_root_claim_type( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def set_subnet_identity( @@ -6476,6 +6581,7 @@ def set_subnet_identity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Sets the identity of a subnet for a specific wallet and network. @@ -6494,6 +6600,7 @@ def set_subnet_identity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6515,6 +6622,7 @@ def set_subnet_identity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def set_weights( @@ -6534,6 +6642,7 @@ def set_weights( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Sets the interneuronal weights for the specified neuron. This process involves specifying the influence or trust @@ -6560,6 +6669,7 @@ def set_weights( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6617,6 +6727,7 @@ def _blocks_weight_limit() -> bool: raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) except Exception as error: return ExtrinsicResponse.from_exception( @@ -6649,6 +6760,7 @@ def _blocks_weight_limit() -> bool: raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) except Exception as error: return ExtrinsicResponse.from_exception( @@ -6673,6 +6785,7 @@ def serve_axon( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. @@ -6693,6 +6806,7 @@ def serve_axon( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6710,6 +6824,7 @@ def serve_axon( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def set_commitment( @@ -6723,6 +6838,7 @@ def set_commitment( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -6744,6 +6860,7 @@ def set_commitment( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6768,6 +6885,7 @@ def set_commitment( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def set_reveal_commitment( @@ -6777,10 +6895,13 @@ def set_reveal_commitment( data: str, blocks_until_reveal: int = 360, block_time: Union[int, float] = 12, + *, + mev_protection: bool = DEFAULT_MEV_PROTECTION, period: Optional[int] = DEFAULT_PERIOD, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -6792,12 +6913,16 @@ def set_reveal_commitment( blocks_until_reveal: The number of blocks from now after which the data will be revealed. Then number of blocks in one epoch. block_time: The number of seconds between each block. + mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect + against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators + decrypt and execute it. If False, submits the transaction directly without encryption. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6818,10 +6943,12 @@ def set_reveal_commitment( netuid=netuid, data_type="TimelockEncrypted", data=data_, + mev_protection=mev_protection, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) response.data = data_ return response @@ -6836,6 +6963,7 @@ def start_call( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start @@ -6853,6 +6981,7 @@ def start_call( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6866,6 +6995,7 @@ def start_call( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def swap_stake( @@ -6884,6 +7014,7 @@ def swap_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -6911,6 +7042,7 @@ def swap_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the inclusion of the transaction. wait_for_finalization: Whether to wait for the finalization of the transaction. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6938,6 +7070,7 @@ def swap_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def toggle_user_liquidity( @@ -6951,6 +7084,7 @@ def toggle_user_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Allow to toggle user liquidity for specified subnet. @@ -6967,6 +7101,7 @@ def toggle_user_liquidity( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -6983,6 +7118,7 @@ def toggle_user_liquidity( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def transfer( @@ -6998,6 +7134,7 @@ def transfer( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Transfer token of amount to destination. @@ -7017,6 +7154,7 @@ def transfer( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7034,6 +7172,7 @@ def transfer( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def transfer_stake( @@ -7050,6 +7189,7 @@ def transfer_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -7070,6 +7210,7 @@ def transfer_stake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7088,6 +7229,7 @@ def transfer_stake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def unstake( @@ -7105,6 +7247,7 @@ def unstake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting @@ -7131,6 +7274,7 @@ def unstake( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7154,6 +7298,7 @@ def unstake( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def unstake_all( @@ -7168,6 +7313,7 @@ def unstake_all( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. @@ -7186,6 +7332,7 @@ def unstake_all( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7239,6 +7386,7 @@ def unstake_all( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def unstake_multiple( @@ -7254,6 +7402,7 @@ def unstake_multiple( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts @@ -7274,6 +7423,7 @@ def unstake_multiple( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7293,6 +7443,7 @@ def unstake_multiple( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def update_cap_crowdloan( @@ -7306,6 +7457,7 @@ def update_cap_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Updates the fundraising cap (maximum total contribution) of a non-finalized crowdloan. @@ -7326,6 +7478,7 @@ def update_cap_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7345,6 +7498,7 @@ def update_cap_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def update_end_crowdloan( @@ -7358,6 +7512,7 @@ def update_end_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Updates the end block of a non-finalized crowdloan campaign. @@ -7378,6 +7533,7 @@ def update_end_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7398,6 +7554,7 @@ def update_end_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def update_min_contribution_crowdloan( @@ -7411,6 +7568,7 @@ def update_min_contribution_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Updates the minimum contribution amount of a non-finalized crowdloan. @@ -7431,6 +7589,7 @@ def update_min_contribution_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7450,6 +7609,7 @@ def update_min_contribution_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) def withdraw_crowdloan( @@ -7462,6 +7622,7 @@ def withdraw_crowdloan( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + wait_for_revealed_execution: bool = True, ) -> ExtrinsicResponse: """ Withdraws a contribution from an active (not yet finalized or dissolved) crowdloan. @@ -7478,6 +7639,7 @@ def withdraw_crowdloan( raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. wait_for_finalization: Whether to wait for finalization of the extrinsic. + wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used. Returns: ExtrinsicResponse: The result object of the extrinsic execution. @@ -7495,4 +7657,5 @@ def withdraw_crowdloan( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=wait_for_revealed_execution, ) diff --git a/tests/unit_tests/extrinsics/test_root.py b/tests/unit_tests/extrinsics/test_root.py index adeac230b4..413f6155e4 100644 --- a/tests/unit_tests/extrinsics/test_root.py +++ b/tests/unit_tests/extrinsics/test_root.py @@ -97,6 +97,7 @@ def test_root_register_extrinsic( wallet=mock_wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) # Assert assert result.success == expected_result diff --git a/tests/unit_tests/extrinsics/test_serving.py b/tests/unit_tests/extrinsics/test_serving.py index 427609cf85..bfb6dea58c 100644 --- a/tests/unit_tests/extrinsics/test_serving.py +++ b/tests/unit_tests/extrinsics/test_serving.py @@ -111,6 +111,7 @@ def test_serve_extrinsic_happy_path( placeholder2=placeholder2, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) # Assert @@ -170,6 +171,7 @@ def test_serve_extrinsic_edge_cases( placeholder2=placeholder2, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) # Assert @@ -228,6 +230,7 @@ def test_serve_extrinsic_error_cases( placeholder2=placeholder2, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) # Assert @@ -308,6 +311,7 @@ def test_serve_axon_extrinsic( axon=mock_axon, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) # Assert @@ -365,6 +369,7 @@ def test_publish_metadata( data=data, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) # Assert assert result.success is True, f"Test ID: {test_id}" diff --git a/tests/unit_tests/extrinsics/test_staking.py b/tests/unit_tests/extrinsics/test_staking.py index a4c69edd09..77ced51faf 100644 --- a/tests/unit_tests/extrinsics/test_staking.py +++ b/tests/unit_tests/extrinsics/test_staking.py @@ -41,6 +41,7 @@ def test_add_stake_extrinsic(mocker): mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) # Asserts diff --git a/tests/unit_tests/extrinsics/test_unstaking.py b/tests/unit_tests/extrinsics/test_unstaking.py index 9611939128..f5c6f578a9 100644 --- a/tests/unit_tests/extrinsics/test_unstaking.py +++ b/tests/unit_tests/extrinsics/test_unstaking.py @@ -37,6 +37,7 @@ def test_unstake_extrinsic(fake_wallet, mocker): mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) # Asserts @@ -145,6 +146,7 @@ def test_unstake_multiple_extrinsic(subtensor, fake_wallet, mocker): mev_protection=DEFAULT_MEV_PROTECTION, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) # Asserts @@ -167,4 +169,5 @@ def test_unstake_multiple_extrinsic(subtensor, fake_wallet, mocker): raise_error=False, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + wait_for_revealed_execution=True, ) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index f671ac8147..714cfc5c18 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -1737,7 +1737,7 @@ async def fake_error_message(): assert result == (False, mocked_format_error_message.return_value) assert result.extrinsic_function == get_function_name() assert result.extrinsic == fake_extrinsic - assert result.extrinsic_fee == None + assert result.extrinsic_fee is None assert result.error is fake_error assert result.data is None @@ -1828,7 +1828,7 @@ async def test_sign_and_send_extrinsic_substrate_request_exception( assert result == (False, str(fake_exception)) assert result.extrinsic_function == get_function_name() assert result.extrinsic == fake_extrinsic - assert result.extrinsic_fee == None + assert result.extrinsic_fee is None assert result.error == fake_exception assert result.data is None @@ -2598,6 +2598,7 @@ async def test_transfer_success(subtensor, fake_wallet, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, + wait_for_revealed_execution=True, ) assert result == mocked_transfer_extrinsic.return_value @@ -2634,6 +2635,7 @@ async def test_register_success(subtensor, fake_wallet, mocker): raise_error=False, wait_for_finalization=True, wait_for_inclusion=True, + wait_for_revealed_execution=True, ) assert result == mocked_register_extrinsic.return_value @@ -2673,6 +2675,7 @@ async def test_set_children(subtensor, fake_wallet, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_set_children_extrinsic.return_value @@ -2803,6 +2806,7 @@ async def test_set_subnet_identity(mocker, subtensor, fake_wallet): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -2911,6 +2915,7 @@ async def test_start_call(subtensor, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -3407,6 +3412,7 @@ async def test_unstake_all(subtensor, fake_wallet, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == fake_unstake_all_extrinsic.return_value @@ -3634,6 +3640,7 @@ async def test_add_liquidity(subtensor, fake_wallet, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -3669,6 +3676,7 @@ async def test_modify_liquidity(subtensor, fake_wallet, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -3702,6 +3710,7 @@ async def test_remove_liquidity(subtensor, fake_wallet, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -3734,6 +3743,7 @@ async def test_toggle_user_liquidity(subtensor, fake_wallet, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -4228,6 +4238,7 @@ async def test_set_auto_stake(subtensor, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -4345,6 +4356,7 @@ async def test_contribute_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4391,6 +4403,7 @@ async def test_create_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4431,6 +4444,7 @@ async def test_crowdloan_methods_with_crowdloan_id_parameter( raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4465,6 +4479,7 @@ async def test_update_cap_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4499,6 +4514,7 @@ async def test_update_end_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4533,6 +4549,7 @@ async def test_update_min_contribution_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4978,6 +4995,7 @@ async def test_claim_root(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_claim_root_extrinsic.return_value @@ -5007,6 +5025,7 @@ async def test_set_root_claim_type(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_set_root_claim_type_extrinsic.return_value @@ -5338,6 +5357,7 @@ async def test_add_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_add_proxy_extrinsic.return_value @@ -5371,6 +5391,7 @@ async def test_announce_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_announce_extrinsic.return_value @@ -5407,6 +5428,7 @@ async def test_create_pure_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_create_pure_proxy_extrinsic.return_value @@ -5453,6 +5475,7 @@ async def test_kill_pure_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_kill_pure_proxy_extrinsic.return_value @@ -5478,6 +5501,7 @@ async def test_poke_deposit(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_poke_deposit_extrinsic.return_value @@ -5512,6 +5536,7 @@ async def test_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_proxy_extrinsic.return_value @@ -5551,6 +5576,7 @@ async def test_proxy_announced(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_proxy_announced_extrinsic.return_value @@ -5584,6 +5610,7 @@ async def test_reject_proxy_announcement(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_reject_announcement_extrinsic.return_value @@ -5617,6 +5644,7 @@ async def test_remove_proxy_announcement(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_remove_announcement_extrinsic.return_value @@ -5642,6 +5670,7 @@ async def test_remove_proxies(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_remove_proxies_extrinsic.return_value @@ -5678,6 +5707,7 @@ async def test_remove_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_remove_proxy_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 82c9e1bf7c..bbf2d8a69c 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1188,6 +1188,7 @@ def test_serve_axon(subtensor, mocker): raise_error=False, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + wait_for_revealed_execution=True, ) assert result == mocked_serve_axon_extrinsic.return_value @@ -1229,6 +1230,7 @@ def test_commit(subtensor, fake_wallet, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result is mocked_publish_metadata.return_value @@ -1289,6 +1291,7 @@ def test_transfer(subtensor, fake_wallet, mocker): keep_alive=True, period=DEFAULT_PERIOD, raise_error=False, + wait_for_revealed_execution=True, ) assert result == mocked_transfer_extrinsic.return_value @@ -1825,6 +1828,7 @@ def test_reveal_weights(subtensor, fake_wallet, mocker): wait_for_inclusion=False, wait_for_finalization=False, mechid=0, + wait_for_revealed_execution=True, ) @@ -2655,6 +2659,7 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): period=DEFAULT_PERIOD, raise_error=False, mev_protection=DEFAULT_MEV_PROTECTION, + wait_for_revealed_execution=True, ) assert result == mock_add_stake_extrinsic.return_value @@ -2699,6 +2704,7 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): period=DEFAULT_PERIOD, raise_error=False, mev_protection=DEFAULT_MEV_PROTECTION, + wait_for_revealed_execution=True, ) assert result == mock_add_stake_extrinsic.return_value @@ -2735,6 +2741,7 @@ def test_add_stake_multiple_success(mocker, fake_wallet, subtensor): wait_for_finalization=False, period=DEFAULT_PERIOD, raise_error=False, + wait_for_revealed_execution=True, ) assert result == mock_add_stake_multiple_extrinsic.return_value @@ -2776,6 +2783,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, + wait_for_revealed_execution=True, ) assert result == mock_unstake_extrinsic.return_value @@ -2817,6 +2825,7 @@ def test_unstake_with_safe_unstaking(mocker, subtensor, fake_wallet): raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, + wait_for_revealed_execution=True, ) assert result == mock_unstake_extrinsic.return_value @@ -2863,6 +2872,7 @@ def test_swap_stake_success(mocker, subtensor, fake_wallet): mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, + wait_for_revealed_execution=True, ) assert result == mock_swap_stake_extrinsic.return_value @@ -2910,6 +2920,7 @@ def test_swap_stake_with_safe_staking(mocker, subtensor, fake_wallet): mev_protection=DEFAULT_MEV_PROTECTION, period=DEFAULT_PERIOD, raise_error=False, + wait_for_revealed_execution=True, ) assert result == mock_swap_stake_extrinsic.return_value @@ -2947,6 +2958,7 @@ def test_unstake_multiple_success(mocker, subtensor, fake_wallet): period=DEFAULT_PERIOD, unstake_all=False, raise_error=False, + wait_for_revealed_execution=True, ) assert result == mock_unstake_multiple_extrinsic.return_value @@ -3002,6 +3014,7 @@ def test_set_weights_with_commit_reveal_enabled(subtensor, fake_wallet, mocker): period=8, raise_error=False, mechid=0, + wait_for_revealed_execution=True, ) assert result == mocked_commit_timelocked_mechanism_weights_extrinsic.return_value @@ -3063,6 +3076,7 @@ def test_set_subnet_identity(mocker, subtensor, fake_wallet): raise_error=False, wait_for_finalization=True, wait_for_inclusion=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -3159,10 +3173,11 @@ def test_start_call(subtensor, mocker): wallet=wallet_name, netuid=netuid, mev_protection=DEFAULT_MEV_PROTECTION, - wait_for_inclusion=True, - wait_for_finalization=False, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=False, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -3720,10 +3735,11 @@ def test_set_children(subtensor, fake_wallet, mocker): netuid=fake_netuid, children=fake_children, mev_protection=DEFAULT_MEV_PROTECTION, - wait_for_finalization=True, - wait_for_inclusion=True, - raise_error=False, period=DEFAULT_PERIOD, + raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_set_children_extrinsic.return_value @@ -3749,10 +3765,11 @@ def test_unstake_all(subtensor, fake_wallet, mocker): netuid=1, rate_tolerance=0.005, mev_protection=DEFAULT_MEV_PROTECTION, - wait_for_inclusion=True, - wait_for_finalization=True, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == fake_unstake_all_extrinsic.return_value @@ -3900,10 +3917,11 @@ def test_add_liquidity(subtensor, fake_wallet, mocker): price_high=Balance.from_tao(130).rao, hotkey_ss58=None, mev_protection=DEFAULT_MEV_PROTECTION, - wait_for_inclusion=True, - wait_for_finalization=True, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -3934,10 +3952,11 @@ def test_modify_liquidity(subtensor, fake_wallet, mocker): liquidity_delta=Balance.from_tao(150), hotkey_ss58=None, mev_protection=DEFAULT_MEV_PROTECTION, - wait_for_inclusion=True, - wait_for_finalization=True, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -3966,10 +3985,11 @@ def test_remove_liquidity(subtensor, fake_wallet, mocker): position_id=position_id, hotkey_ss58=None, mev_protection=DEFAULT_MEV_PROTECTION, - wait_for_inclusion=True, - wait_for_finalization=True, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -3997,10 +4017,11 @@ def test_toggle_user_liquidity(subtensor, fake_wallet, mocker): netuid=netuid, enable=enable, mev_protection=DEFAULT_MEV_PROTECTION, - wait_for_inclusion=True, - wait_for_finalization=True, period=DEFAULT_PERIOD, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -4457,6 +4478,7 @@ def test_set_auto_stake(subtensor, mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert result == mocked_extrinsic.return_value @@ -4539,6 +4561,7 @@ def test_contribute_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4584,6 +4607,7 @@ def test_create_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4623,6 +4647,7 @@ def test_crowdloan_methods_with_crowdloan_id_parameter( raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4656,6 +4681,7 @@ def test_update_cap_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4689,6 +4715,7 @@ def test_update_end_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -4722,6 +4749,7 @@ def test_update_min_contribution_crowdloan(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_extrinsic.return_value @@ -5130,6 +5158,7 @@ def test_claim_root(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_claim_root_extrinsic.return_value @@ -5158,6 +5187,7 @@ def test_set_root_claim_type(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_set_root_claim_type_extrinsic.return_value @@ -5482,6 +5512,7 @@ def test_add_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_add_proxy_extrinsic.return_value @@ -5514,6 +5545,7 @@ def test_announce_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_announce_extrinsic.return_value @@ -5549,6 +5581,7 @@ def test_create_pure_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_create_pure_proxy_extrinsic.return_value @@ -5594,6 +5627,7 @@ def test_kill_pure_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_kill_pure_proxy_extrinsic.return_value @@ -5618,6 +5652,7 @@ def test_poke_deposit(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_poke_deposit_extrinsic.return_value @@ -5651,6 +5686,7 @@ def test_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_proxy_extrinsic.return_value @@ -5689,6 +5725,7 @@ def test_proxy_announced(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_proxy_announced_extrinsic.return_value @@ -5721,6 +5758,7 @@ def test_reject_proxy_announcement(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_reject_announcement_extrinsic.return_value @@ -5753,6 +5791,7 @@ def test_remove_proxy_announcement(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_remove_announcement_extrinsic.return_value @@ -5777,6 +5816,7 @@ def test_remove_proxies(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_remove_proxies_extrinsic.return_value @@ -5812,6 +5852,7 @@ def test_remove_proxy(mocker, subtensor): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + wait_for_revealed_execution=True, ) assert response == mocked_remove_proxy_extrinsic.return_value From 10e88a6a1c452a0d3db74c328684d36eaeac83a9 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Wed, 3 Dec 2025 17:55:53 -0800 Subject: [PATCH 58/67] improvement for utils --- bittensor/core/extrinsics/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 2db5bffe39..d347a8be08 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -332,7 +332,7 @@ def get_event_attributes_by_event_name(events: list, event_name: str) -> Optiona def post_process_mev_response( response: "ExtrinsicResponse", - revealed_name: str, + revealed_name: Optional[str], revealed_extrinsic: Optional["ExtrinsicReceipt | AsyncExtrinsicReceipt"], raise_error: bool = False, ) -> None: @@ -368,7 +368,7 @@ def post_process_mev_response( # when main extrinsic is successful but revealed extrinsic is not found in the chain. if revealed_extrinsic is None: response.success = False - response.message = "Result event not found in chain." + response.message = "MeV protected extrinsic does not contain related event." response.error = RuntimeError(response.message) # when main extrinsic is successful but revealed extrinsic is not successful. From 09d891bcfcc2974b8a409cdc8d80676d8554fe76 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Wed, 3 Dec 2025 18:31:08 -0800 Subject: [PATCH 59/67] fix `find_revealed_extrinsic` --- bittensor/core/extrinsics/asyncex/mev_shield.py | 4 ++-- bittensor/core/extrinsics/mev_shield.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index d18ff5ff07..bc8505bb32 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -27,7 +27,7 @@ async def find_revealed_extrinsic( event_hash_id: str, start_block_hash: str, blocks_ahead: int = 5, -) -> Optional[tuple[str, "AsyncExtrinsicReceipt"]]: +) -> tuple[str, "ExtrinsicReceipt"] | tuple[None, None]: """ Searches for an extrinsic containing a specific MEV Shield event in subsequent blocks. @@ -77,7 +77,7 @@ async def find_revealed_extrinsic( await subtensor.wait_for_block() - return None + return None, None async def submit_encrypted_extrinsic( diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index f965fbfecd..6fde016e7d 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -27,7 +27,7 @@ def find_revealed_extrinsic( event_hash_id: str, start_block_hash: str, blocks_ahead: int = 5, -) -> Optional[tuple[str, "ExtrinsicReceipt"]]: +) -> tuple[str, "ExtrinsicReceipt"] | tuple[None, None]: """ Searches for an extrinsic containing a specific MEV Shield event in subsequent blocks. @@ -77,7 +77,7 @@ def find_revealed_extrinsic( subtensor.wait_for_block() - return None + return None, None def submit_encrypted_extrinsic( From c6a9b2ee55a16e844b01f033e475c5b72205a9b3 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Wed, 3 Dec 2025 18:33:52 -0800 Subject: [PATCH 60/67] typo --- bittensor/core/extrinsics/asyncex/mev_shield.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index bc8505bb32..613c2d5e93 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -27,7 +27,7 @@ async def find_revealed_extrinsic( event_hash_id: str, start_block_hash: str, blocks_ahead: int = 5, -) -> tuple[str, "ExtrinsicReceipt"] | tuple[None, None]: +) -> tuple[str, "AsyncExtrinsicReceipt"] | tuple[None, None]: """ Searches for an extrinsic containing a specific MEV Shield event in subsequent blocks. From 4eda7c5356a69b2f1f6252a9ccf01e63eab80f0c Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 4 Dec 2025 11:14:05 -0800 Subject: [PATCH 61/67] update `post_process_mev_response` --- bittensor/core/extrinsics/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index d347a8be08..db17f9c359 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -379,5 +379,8 @@ def post_process_mev_response( ) response.error = RuntimeError(response.message) - if response.error and raise_error: - raise response.error + if response.error: + if raise_error: + raise response.error + else: + response.with_log() From 4bfcbabf4528d9338228474189072e23a36a52be Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 4 Dec 2025 11:14:35 -0800 Subject: [PATCH 62/67] improve `ExtrinsicResponse.with_log` --- bittensor/core/types.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/bittensor/core/types.py b/bittensor/core/types.py index aae3bafb5d..fa55c53d9e 100644 --- a/bittensor/core/types.py +++ b/bittensor/core/types.py @@ -538,7 +538,17 @@ def with_log( ExtrinsicResponse instance. """ if self.message: - getattr(logging, level)(self.message) + if level in ["trace", "error"]: + message = f"[red]{self.message}[/red]" + elif level == "info": + message = f"[blue]{self.message}[/blue]" + elif level == "warning": + message = f"[yellow]{self.message}[/yellow]" + elif level == "success": + message = f"[green]{self.message}[/green]" + else: + message = self.message + getattr(logging, level)(message) return self From 7b890954c0d292aa50432862c935410311cda4a3 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 4 Dec 2025 11:14:59 -0800 Subject: [PATCH 63/67] add logging in `find_revealed_extrinsic` --- bittensor/core/extrinsics/asyncex/mev_shield.py | 1 + bittensor/core/extrinsics/mev_shield.py | 1 + 2 files changed, 2 insertions(+) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index 613c2d5e93..b88a44866b 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -75,6 +75,7 @@ async def find_revealed_extrinsic( extrinsic_idx=event["extrinsic_idx"], ) + logging.debug(f"No {event_names} event found in block {current_block_number}.") await subtensor.wait_for_block() return None, None diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index 6fde016e7d..37ba67b973 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -75,6 +75,7 @@ def find_revealed_extrinsic( extrinsic_idx=event["extrinsic_idx"], ) + logging.debug(f"No {event_names} event found in block {current_block_number}.") subtensor.wait_for_block() return None, None From d609a705408ca4c24761011c7ac15a2d34bc00cd Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 4 Dec 2025 11:16:36 -0800 Subject: [PATCH 64/67] imports --- bittensor/core/async_subtensor.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 88ef814d56..b744eed821 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -99,8 +99,8 @@ ) from bittensor.core.extrinsics.asyncex.serving import ( publish_metadata_extrinsic, + serve_axon_extrinsic ) -from bittensor.core.extrinsics.asyncex.serving import serve_axon_extrinsic from bittensor.core.extrinsics.asyncex.staking import ( add_stake_extrinsic, add_stake_multiple_extrinsic, @@ -124,10 +124,10 @@ from bittensor.core.metagraph import AsyncMetagraph from bittensor.core.settings import ( DEFAULT_MEV_PROTECTION, - version_as_int, DEFAULT_PERIOD, - TYPE_REGISTRY, TAO_APP_BLOCK_EXPLORER, + TYPE_REGISTRY, + version_as_int, ) from bittensor.core.types import ( BlockInfo, @@ -150,23 +150,24 @@ ) from bittensor.utils.balance import ( Balance, - fixed_to_float, check_balance_amount, + fixed_to_float, ) from bittensor.utils.btlogging import logging from bittensor.utils.liquidity import ( + LiquidityPosition, calculate_fees, get_fees, - tick_to_price, price_to_tick, - LiquidityPosition, + tick_to_price, ) if TYPE_CHECKING: + from async_substrate_interface import AsyncQueryMapResult from async_substrate_interface.types import ScaleObj from bittensor_wallet import Keypair, Wallet + from bittensor.core.axon import Axon - from async_substrate_interface import AsyncQueryMapResult class AsyncSubtensor(SubtensorMixin): From 97b51aca8c849fa165bd881b5a539f1bddcb6235 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 4 Dec 2025 11:31:46 -0800 Subject: [PATCH 65/67] ruff --- bittensor/core/async_subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index b744eed821..478159d98b 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -99,7 +99,7 @@ ) from bittensor.core.extrinsics.asyncex.serving import ( publish_metadata_extrinsic, - serve_axon_extrinsic + serve_axon_extrinsic, ) from bittensor.core.extrinsics.asyncex.staking import ( add_stake_extrinsic, From 18113717b132efbcf1be79f8562b4669ab2959b3 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 4 Dec 2025 12:00:21 -0800 Subject: [PATCH 66/67] just ck for unlock wallet --- bittensor/core/extrinsics/asyncex/mev_shield.py | 4 +--- bittensor/core/extrinsics/mev_shield.py | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/mev_shield.py b/bittensor/core/extrinsics/asyncex/mev_shield.py index b88a44866b..40feffeb23 100644 --- a/bittensor/core/extrinsics/asyncex/mev_shield.py +++ b/bittensor/core/extrinsics/asyncex/mev_shield.py @@ -135,9 +135,7 @@ async def submit_encrypted_extrinsic( """ try: if not ( - unlocked := ExtrinsicResponse.unlock_wallet( - wallet, raise_error, unlock_type="both" - ) + unlocked := ExtrinsicResponse.unlock_wallet(wallet, raise_error) ).success: return unlocked diff --git a/bittensor/core/extrinsics/mev_shield.py b/bittensor/core/extrinsics/mev_shield.py index 37ba67b973..7346da363c 100644 --- a/bittensor/core/extrinsics/mev_shield.py +++ b/bittensor/core/extrinsics/mev_shield.py @@ -135,9 +135,7 @@ def submit_encrypted_extrinsic( """ try: if not ( - unlocked := ExtrinsicResponse.unlock_wallet( - wallet, raise_error, unlock_type="both" - ) + unlocked := ExtrinsicResponse.unlock_wallet(wallet, raise_error) ).success: return unlocked From 895aba77a98185f880171153952b0e5329ce07c5 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 4 Dec 2025 12:15:46 -0800 Subject: [PATCH 67/67] tab --- bittensor/core/async_subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 478159d98b..58825cd451 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -7841,8 +7841,8 @@ async def set_root_claim_type( new_root_claim_type: The new root claim type to set. Can be: - String: "Swap" or "Keep" - RootClaimType: RootClaimType.Swap, RootClaimType.Keep - - Dict: {"KeepSubnets": {"subnets": [1, 2, 3]}} - - Callable: RootClaimType.KeepSubnets([1, 2, 3]) + - Dict: {"KeepSubnets": {"subnets": [1, 2, 3]}} + - Callable: RootClaimType.KeepSubnets([1, 2, 3]) mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators decrypt and execute it. If False, submits the transaction directly without encryption.