Skip to main content
SUBMIT A PRSUBMIT AN ISSUElast edit: Dec 08, 2025

Working with Proxies

This page covers each step in the use of proxy wallets as a security feature for Bittensor operations:

  • Creating proxy relationships between existing wallets
  • Executing transactions with 0-day proxy wallets.
  • Announcing and then executing transactions with a non-zero delay period.
  • Removing proxy relationships

See:

Introduction

Security Considerations

Proxy wallets are a powerful security feature, but to get the full benefits, it is critical to observe good key security. When one wallet serves as proxy for another (the 'safe wallet'), both the safe wallet and the proxy wallet have their own full coldkey keypair (the public key which goes into the wallet's address, and the private key, which is recoverable using the seed phrase), and must be handled with proper care.

Generally, the safe wallet should be given the maximum security possible, whereas the proxy wallet (if it is carefully limited in its permissions), can be handled in a more convenient, less secure way. For example, a proxy might be loaded into a less trusted compute runtime, whereas the safe wallet's coldkey private key/seed phrase should never be loaded into any but the most absolutely secure device). However, depending on the proxy's configuration, compromise of a proxy wallet's coldkey can still be disastrous. For example, a proxy with ProxyType:any and delay:0 can immediately perform any operation on behalf of the safe wallet, so leaking such a proxy key is just as bad as leaking the safe wallet key.

Before executing any operations with any coldkeys holding TAO on Bittensor main network, carefully think through the desired end result and the steps required to achieve it.

See: Coldkey and Hotkey Workstation Security.

Prerequisites

Practice/Dev

To follow along with the below examples for practice, you have two options:

Main Network

Once you have practiced on a local or test chain, and you are ready to execute these operations on Bittensor main network (finney), you will need two wallets and enough TAO to cover some small fees:

  • The safe wallet or 'real account' that will be protected by the proxy.
  • The proxy wallet, which will act on behalf of the safe wallet.
fee

The delegate account must hold enough funds to cover transaction fees, which are approximately 25 Rao (0.000025 TAO).

See: Fees

Add a Proxy Relationship

Add a proxy record on the blockchain to designate a proxy wallet for your safe wallet.

consider security!

Note that this operation requires the safe wallet's coldkey private key, which is a maximally sensitive and valuable cryptographic secret.

For any wallet with real-value TAO (i.e. TAO on Bittensor's main network, finney), coldkey private keys and seed phrases should be handled with utmost care, only on dedicated coldkey workstations.

See: Coldkey and Hotkey Workstation Security.

info

Multiple proxy relationships can exist between a pair of wallets, as long as each proxy entry uses a different ProxyType. Attempting to register a duplicate entry with the same delegate and ProxyType will result in a proxy.Duplicate error.

Add the on-chain proxy relationship

Run btcli proxy add to create a proxy relationship between existing wallets on-chain.

Note that --wallet.name specifies the safe wallet, since the private key must be loaded in for the safe wallet, not the proxy. This makes sense because it is the safe wallet that is delegating the authority to order transactions to the proxy wallet, so it must be authenticated with the private key using its encryption password.

btcli proxy add \
--wallet.name SAFE_WALLET_NAME \
--delegate PROXY_WALLET_COLDKEY_ss58 \ # Proxy wallet's coldkey
--proxy-type PROXY_TYPE \

Parameters:

  • --wallet.name: Your wallet name (the real account that will authorize the proxy)
  • --delegate: The SS58 address of the proxy (i.e. the delegate of transaction power)
  • --proxy-type: The type of proxy relationship (e.g., Staking, Transfer, Any, etc.)
  • --delay: Optional delay in blocks (0 for immediate execution)

For our example, we'll use two wallets called PracticeSafeWallet and PracticeProxy. To follow along, create two new wallets with these names and substitute their coldkey ss58 addresses:

  • PracticeSafeWallet: 5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt
  • PracticeProxy: 5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe

To give PracticeProxy the ability to order small transfers from PracticeSafeWallet's balance immediately (with 0 delay), we'll use the following comand:

btcli proxy add \
--wallet.name PracticeSafeWallet \
--delegate 5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe \
--proxy-type SmallTransfer \
--network test
✅ Your extrinsic has been included as 5951841-6
Added proxy delegatee '5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe' from delegator
'5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt' with proxy type 'SmallTransfer' with delay 0.
Would you like to add this to your address book? [y/n]: y

BTCLI's proxy address book

Use btcli config add-proxy to configure your local BTCLI with a proxy relationship. Make sure to follow the instructions carefully depending on whether:

  1. You are using a proxy relationship between pre-existing wallets, as described on this page. This covers most use cases for proxies.
  2. You are using a pure proxy. See pure proxies.

View all saved proxies with:

btcli config proxies
 Name                    Address                 Spawner/Delegator       Proxy Type      Delay   Note
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
practice-proxying 5CngkPSSnhK7ot6zFv3Q… 5GrwvaEF5zXb26Fz9rcQ… Any 0 always be awesome
practice-small-transfers 5GrwvaEF5zXb26Fz9rcQ… 5FLSigC9HGRKVhB9FiEo… SmallTransfer 0 small transfers only

Check an Account’s Proxies

You can check which proxies are associated with an account to see their delegate addresses, proxy types, and any configured delays. To do this:

To check proxies in BTCLI, you can view your local address book:

btcli config proxies

This displays all proxies you've saved to your local address book.

On-chain proxy query

BTCLI does not currently provide a command to query on-chain proxy state directly. To view all proxies registered on-chain for an account, use the SDK's get_proxies_for_real_account() method or query via Polkadot.js Apps.

Execute a 0-Delay Proxy Call

A proxy wallet that is set up with a delay of 0 can execute transactions allowed by its proxy type simply by declaring which real account they are acting as proxy for.

consider security!

This operation will be run in a coldkey workstation that is set up for the proxy wallet, not the safe wallet/real account. For main network (finney) wallets, the safe wallet's coldkey private key should never be loaded onto the proxy workstation, otherwise we undermine the security advantage of the proxy relationship. The safe wallet's coldkey private key/seed phrase should be kept in cold storage as muchas possible, and should only be loaded into dedicated, highly secure, code environments provisioned specifically for that purpose.

Many btcli commands support the --proxy flag to proxy an operation on behalf of another wallet.

terminology and parameter names

The language here may be counter-intuitive, in that the --proxy flag specifies the wallet being proxied.

The wallet specified by --wallet.name is actually the wallet we normally call "the proxy", and --proxy specifies the safe wallet or 'real account'. It makes more sense if you think of the --proxy flag as specifying that the operation is being called by proxy for the wallet that follows, i.e., the safe wallet.

More to the point, we can logically infer that it must be the case that --wallet.name refers to the proxy, and the ss58 supplied (in the --proxy field) must refer to the safe wallet, since this command is meant to be run by the proxy, protecting the safe wallet. Therefore, the proxy's private key must be present and unlocked, not the safe wallet's, which should remain in cold storage.

This command will transfer 18 TAO from PracticeSafeWallet to a third wallet, Miner.


btcli wallet transfer \
--wallet.name PracticeProxy \
--proxy 5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt \
--destination 5DA7UsaYbk1UnhhtTxqpwdqjuxhQ2rW7D6GTN1S1S5tC2NRV \
--amount 0.333 \
--network test

Proxy params:

  • --wallet.name: The proxy wallet that signs the transaction on behalf of the real account.
  • --proxy: The real account's SS58 address (or proxy name from address book)
Initiating transfer on network: test
Do you want to transfer:
amount: 0.3330 τ
from: PracticeProxy : 5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe
to: 5DA7UsaYbk1UnhhtTxqpwdqjuxhQ2rW7D6GTN1S1S5tC2NRV
for fee: 0.0000 τ
Transferring is not the same as staking. To instead stake, use btcli stake add instead.
Proceed with transfer? [y/n]: y
Enter your password:
Decrypting...
✅ Finalized
Block Hash: 0xc8f8cba3395cd34d6dd2e2bc3b8e1b5e6b6eeb60754dac398b08bca735a6a32d
Balance:
98.7739 τ ➡ 98.4409 τ
Using saved proxies

If you saved a proxy to your address book with btcli config add-proxy, you can reference it by name:

btcli wallet transfer \
--wallet.name PracticeProxy \
--proxy PracticeSafeWallet \
--destination 5DA7UsaYbk1UnhhtTxqpwdqjuxhQ2rW7D6GTN1S1S5tC2NRV \
--amount 0.333 \
--network test

Remove a Proxy

Removing a proxy revokes the delegate’s permission to act on behalf of the primary account, effectively ending the proxy relationship on-chain. To remove a proxy:

btcli proxy remove \
--wallet.name WALLET_NAME \
--delegate DELEGATE_ADDRESS \
--proxy-type Staking \
--delay 0

Parameters:

  • --wallet.name: Your wallet name (the real account that authorized the proxy)
  • --delegate: The SS58 address of the delegate account to remove
  • --proxy-type: Must match the proxy type used when adding
  • --delay: Must match the delay value used when adding

For example, let's remove the 0-delay SmallTransfer proxy relationship we established above between our PracticeSafeWallet and PracticeProxy wallets.

btcli proxy remove \
--wallet.name PracticeSafeWallet \
--delegate 5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe \
--proxy-type SmallTransfer \
--delay 0
This will remove a proxy of type SmallTransfer for delegate 5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe.Do you want
to proceed? [y/n]: y
✅Success!
Removal is immediate

Unlike delayed execution, removing a proxy takes effect immediately, regardless of any delay configured on the proxy.

info

The delegate_ss58, proxy_type, and delay parameters must exactly match those used when the proxy was added. The delay parameter is an identifier for the specific proxy relationship, not a delay before removal takes effect (removal is immediate). Use get_proxies_for_real_account() to retrieve the exact parameters for existing proxies.

Remove all proxies

Use this to remove all proxies associated with an account.

BTCLI does not currently provide a single command to remove all proxies at once. You must remove each proxy individually using btcli proxy remove.

SDK alternative

To remove all proxies in one operation, use the SDK's remove_proxies() method.

Announce and Execute a Delayed Proxy Call

If a proxy wallet has been given proxy powers to make a transaction with a delay, they must announce the call beforehand, and then wait the delay interval (specified by the delay parameter when the proxy relationship is created).

For example, the following command gives PracticeProxy the ability to make large transfers, but only after an announcement-delay period of 100 blocks:

btcli proxy add \
--wallet.name PracticeSafeWallet \
--delegate 5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe \
--proxy-type Transfer \
--delay 100 \
--network test
Added proxy delegatee '5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe' from delegator
'5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt' with proxy type 'Transfer' with delay 100.

Generate call hash

Announcing a delayed proxy call requires the hash of the call that you intend to execute. Therefore, you must first generate the call hash of the transaction you want to carry out. To generate the call hash:

When using --announce-only, BTCLI automatically generates and stores the call hash for you. You don't need to manually generate it.

Announce a proxy call

Announcing a proxy call publishes the hash of a proxy-call that will be made in the future. To announce a delayed call:

For delayed proxies, first announce the call using the --announce-only flag:

# The call hash is automatically generated and saved
btcli wallet transfer \
--wallet.name PracticeProxy \
--proxy 5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt \
--destination 5DA7UsaYbk1UnhhtTxqpwdqjuxhQ2rW7D6GTN1S1S5tC2NRV \
--amount 0.333 \
--network test \
--announce-only


Do you want to transfer:
amount: 0.3330 τ
from: PracticeProxy : 5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe
to: 5DA7UsaYbk1UnhhtTxqpwdqjuxhQ2rW7D6GTN1S1S5tC2NRV
for fee: 0.0000 τ
Transferring is not the same as staking. To instead stake, use btcli stake add instead.
Proceed with transfer? [y/n]: y
Enter your password:
Decrypting...
Added entry b'D\x99-\x9d\xa2:BqnQ\xb7\xb6\x99M\xc8\xe1\xd6;\xb2\x810\x15\x82y\xb3XLD\x90#\xd92' at block 5953000 to your
ProxyAnnouncements address book.
✅ Finalized
Block Hash: 0x1c6378ee38b8c27f161b646125ec301f1aa52bffd63b090ec0c0876c9cc56ba5
Balance:
98.4409 τ ➡ 98.4409 τ

What this does:

  • Creates and announces the call on-chain
  • Saves the announcement details to your local database
  • Does NOT execute the operation immediately
  • The real account can reject it during the delay period

After announcing:

  1. Wait for the configured delay period (in blocks) to pass
  2. The real account has the option to reject the announcement
  3. Execute the call after the delay expires (see next step)

Execute a delayed proxy call

After the announcement waiting period has passed, the delegate account can now execute the proxy if the real account did not reject it. Attempting to execute the proxy before the waiting period passes returns a proxy.Unannounced error. To execute a delayed proxy call:

After the delay period has passed, execute the announced call:

btcli proxy execute \
--wallet.name PracticeProxy \
--proxy 5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt \
--real 5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt
--network test

btcli wallet transfer \
--wallet.name PracticeProxy \
--proxy 5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt \
--destination 5DA7UsaYbk1UnhhtTxqpwdqjuxhQ2rW7D6GTN1S1S5tC2NRV \
--amount 0.333 \
--network test \
--announce-only

How it works:

  • Retrieves the previously announced call from your local database
  • Verifies the delay period has passed
  • Executes the call on-chain
  • Clears the announcement

Manual execution: If you need to specify call details manually:

btcli proxy execute \
--wallet.name PracticeProxy \
--proxy 5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt \
--real 5CS9x5NsPHpb2THeS92zBYCSSk4MFoQjjx76DB8bEzeJTTSt \
--delegate 5CZmB94iEG4Ld7JkejAWToAw7NKEfV3YZHX7FYaqPGh7isXe \
--call-hash 0x1c6378ee38b8c27f161b646125ec301f1aa52bffd63b090ec0c0876c9cc56ba5 \
--network test
Automatic tracking

BTCLI automatically tracks announcements you make with --announce-only in a local database, making execution easier.

info
  • The call details on the executed proxy must exactly match the original announcement. Any change to the call or call hash will result in a proxy.Unannounced error.
  • Once a delayed proxy call is executed, its announcement is cleared. To execute another proxy with the same details, you must create a new announcement and wait for the waiting period to pass.

Troubleshooting

  • proxy.Duplicate: A proxy with the same configuration already exists on the real account. See source code: Duplicate error.
  • proxy.Unannounced: A non-zero delay proxy requires an announcement; announce and wait the delay. See source code: Unannounced error.
  • proxy.Unproxyable/system.CallFiltered: The call is not permitted under the current ProxyType. See source code: Unproxyable error.
  • proxy.TooMany: You exceeded MaxProxies or MaxPending. Remove unused proxies/announcements. See source code: TooMany error.
  • proxy.NotProxy: Ensure you're submitting from the delegate account and referencing the correct real account. See source code: NotProxy error.
  • Token.FundsUnavailable: Ensure that your real account has enough available funds to cover the transaction.