Skip to main content
SUBMIT A PRSUBMIT AN ISSUElast edit: Nov 21, 2025

Understanding Pure Proxies

This page covers creating a pure proxy and executing a call using a pure proxy.


Pure proxies are a specialized type of proxy account in Bittensor that provide enhanced security and isolation for complex delegation scenarios. Unlike standard proxies that use existing accounts, pure proxies create new, keyless accounts that can only operate through their delegator relationship.

This page walks you through creating a pure proxy account, executing a transfer through it, and eventually removing it using the Polkadot.js web app. You will set up a new delegate account, add it to your Polkadot.js accounts, and use it to execute transactions on the blockchain.

Overview of pure proxies

Pure proxies are keyless, non-deterministic accounts that are created fresh using the createPure extrinsic. They represent a unique approach to account delegation where:

  • The proxy account has no private key and cannot sign transactions independently
  • The proxy can only act through its delegator—all operations must be initiated by the delegator
  • The account is completely isolated and cannot escalate its own permissions

Unlike standard proxies, where the delegate can access the delegator’s funds to execute calls on their behalf, pure proxies operate differently. A pure proxy account must hold its own funds, while the spawner account acts as an Any proxy for it—signing and authorizing transactions on the proxy’s behalf.

When to use pure proxies

Pure proxies are valuable when you want to keep your real account secure by reducing direct key exposure to the blockchain. They provide a keyless, flexible account that enables permissionless management and are especially effective for multisigs, since they allow updates to membership or thresholds without changing the account address.

Transaction flow in pure proxies

All transactions involving a pure proxy must be signed by the spawner account. Once signed, the transaction is executed on-chain as if it originated directly from the pure proxy. Unlike standard proxies, a pure proxy must hold its own funds to cover fees or transfers. The spawner then acts as an Any proxy, handling the signing and authorization of calls, but the balance used comes from the pure proxy's account.

When submitting calls with the proxy(real, forceProxyType, call) extrinsic, the pure proxy account is passed as the real argument, while the delegator signs the transaction. This effectively reverses the usual proxy relationship where the proxy account only authorizes the transaction, while the real account appears as the origin on chain.

info

You can modify who signs for a pure proxy by assigning another account as its Any proxy. This is done by executing a proxy call that creates a standard proxy with the Any proxy type. The new account can then sign on behalf of the pure proxy—for example, when updating signers in a multisig wallet. See source code: pure proxy account generation.

Prerequisites

Before creating a pure proxy, ensure you have a spawner account that will initialize and control the pure proxy.

Create a pure proxy

The proxy::createPure extrinsic creates a pure proxy. See source code: createPure implementation.

Use this operation to generate a pure proxy account:

import bittensor as bt
from bittensor.core.chain_data.proxy import ProxyType

subtensor = bt.Subtensor()

spawner = bt.Wallet(name="WALLET_NAME")

response = subtensor.create_pure_proxy(
wallet=spawner,
proxy_type=ProxyType.Any,
delay=0,
index=0,
)

if response.success:
pure_account = response.data.get("pure_account")
spawner_address = response.data.get("spawner")
height = response.data.get("height")
ext_index = response.data.get("ext_index")

print(f"✓ Pure proxy created!")
print(f" Pure proxy address: {pure_account}")
print(f" Spawner: {spawner_address}")
print(f" Block: {height}")
print(f" Extrinsic index: {ext_index}")
else:
print(f"✗ Failed: {response.message}")
tip
  • Record the block number and extrinsic index where the pure proxy was created. These values are required to kill the proxy. You can also retrieve the block height and extrinsic details by searching your transaction on the Tao.app block explorer.
  • The proxy type can be provided either by importing and using the ProxyType enum or by passing the proxy type as a string.

Creating a pure proxy adds the spawner account as the first delegate for that proxy. Additional delegates can also be added by registering new proxy entries from the pure proxy account, each specifying the delegate account, proxy type, etc.

Executing calls via a pure proxy

When executing a pure proxy, the proxy account initiates the transaction, but it is signed and authorized by the spawner account. In practice, the proxy account is treated as the real account during execution.

The following example shows how to execute a transfer call using a pure proxy. To do this:

import bittensor as bt
from bittensor.core.chain_data.proxy import ProxyType
from bittensor.core.extrinsics.pallets import Balances

subtensor = bt.Subtensor()

proxy_account = "PROXY_ACCOUNT_ADDRESS" # address of the proxy account
spawner_address = bt.Wallet(name="WALLET_NAME") # name of the signer/spawner wallet
recipient_wallet = "RECIPIENT_WALLET"

# Create a transfer call
transfer_amount = bt.Balance.from_tao(1.0)
transfer_call = Balances(subtensor).transfer_keep_alive(
dest=recipient_wallet,
value=transfer_amount.rao,
)

# Execute the call through the proxy
response = subtensor.proxy(
wallet=spawner_address, # spawner signs the transaction
real_account_ss58=proxy_account, # proxy acts as real account
force_proxy_type=ProxyType.Any,
call=transfer_call,
)

if response.success:
print(f"✓ Transfer executed through proxy!")
print(f" Transferred {transfer_amount} from {proxy_account[:10]}...")
else:
print(f"✗ Failed: {response.message}")
Building a call

Before executing a proxy through the SDK, you must first build the inner call that represents the action you want the chain—or proxy—to perform. This can be done by creating a generic call manually (for example using subtensor.compose_call()) or by using the SDK’s built-in call builders from the relevant pallet.

To build a call using the SDK call builder, import the relevant pallet class (e.g., ProxyBalancesSubtensorModule, etc.) from bittensor.core.extrinsics.pallets, instantiate it with your subtensor instance, then call the method for the extrinsic you need. For example:

from bittensor.core.extrinsics.pallets import Proxy
from bittensor.core.extrinsics.pallets import Balances

# using the Proxy pallet class
Proxy(subtensor).add_proxy(...)

# using the Balances pallet class
Balances(subtensor).transfer_keep_alive(...)
warning

Ensure the pure proxy account holds enough funds to cover both the transfer and transaction fees.


Kill a pure proxy

Killing a pure proxy requires the proxy account address, the spawner account, and the proxy's complete creation details—the block height, extrinsic index, and disambiguation index. Once executed, the pure proxy is permanently removed, and any funds remaining in the proxy account are lost.

Pure proxies are killed using the killPure extrinsic as shown. See source code: killPure implementation:

import bittensor as bt
from bittensor.core.chain_data.proxy import ProxyType

subtensor = bt.Subtensor()

spawner_address = bt.Wallet(name="WALLET_NAME") # signer/spawner

proxy_address = "PROXY_ADDRESS"
proxy_type = ProxyType.Any
index = 0
height = "BLOCK_HEIGHT"
ext_index = "EXTRINSIC_INDEX"

# Kill the pure proxy
response = subtensor.kill_pure_proxy(
wallet=spawner_address,
pure_proxy_ss58=proxy_address,
spawner=spawner_address.coldkeypub.ss58_address, # Valid only when the spawner account signs the extrinsic
proxy_type=proxy_type,
index=index, # the disambiguation index
height=height, # the block height where the proxy was created
ext_index=ext_index, # the extrinsic index of the `Proxy.PureCreated` transaction
)

if response.success:
print("✓ Pure proxy killed successfully!")
else:
print(f"✗ Failed: {response.message}")
warning

Killing a pure proxy deletes the account the proxy account from the blockchain. Any funds in the account are permanently lost.

Removing vs. Killing a Pure Proxy

Killing a pure proxy permanently deletes the account and releases its reserved deposit. Removing a proxy, on the other hand, only detaches it from the account that initiates the transaction. The pure proxy remains on-chain and can still be used if it has other proxies linked to it, but it becomes inactive once all proxy relationships are removed. For more information on how to remove a proxy, see Remove a proxy.

Troubleshooting

  • Token.FundsUnavailable: Ensure that the pure proxy account has been funded and has enough funds to cover the transfer.
  • proxy.NotProxy: Ensure you're executing the pure proxy correctly—from the creator account and referencing the pure proxy account as real. See source code: NotProxy error.
  • Proxy.NoPermission: The killPure call is not permitted under the current. See source code: NoPermission error.
  • system.CallFiltered: The call is not permitted under the current ProxyType.