Skip to content

Proxy System Overview

Roman edited this page Mar 31, 2026 · 2 revisions

Proxy System Overview

Terminology and Relationships

graph LR
    subgraph "ProxyDefinition stored in Proxies storage, keyed by delegator"
        PD["delegate: AccountId
        proxy_type: ProxyType
        delay: BlockNumber"]
    end

    Delegator["Delegator (aka real)
    ───────────────
    Owns the account.
    Creates/removes proxies.
    Pays proxy deposit."]

    Delegate["Delegate
    ───────────────
    Signs transactions
    on behalf of delegator.
    Limited by proxy_type."]

    Delegator -- "add_proxy / remove_proxy" --> PD
    PD -- "authorizes" --> Delegate
    Delegate -- "proxy(real, call)" --> Delegator
Loading

Regular Proxy Lifecycle

sequenceDiagram
    participant D as Delegator (real)
    participant Chain as On-chain Storage
    participant P as Delegate

    Note over D: Signs with own key
    D->>Chain: add_proxy(delegate, proxy_type, delay)
    Note over Chain: Proxies[delegator] += ProxyDefinition<br/>Reserve deposit from delegator

    alt delay = 0 (immediate)
        Note over P: Signs with delegate key
        P->>Chain: proxy(real=delegator, call)
        Chain->>Chain: find_proxy -> check proxy_type.filter(call) -> execute as delegator
    else delay > 0 (deferred)
        P->>Chain: announce(real=delegator, call_hash)
        Note over Chain: Announcements[delegate] += {real, call_hash, height}<br/>Reserve announcement deposit from delegate
        Note over Chain: Wait >= delay blocks
        Note over D: Optional: can veto
        D-->>Chain: reject_announcement(delegate, call_hash)
        Note over P,Chain: After delay blocks pass:
        P->>Chain: proxy_announced(delegate, real, call)
        Chain->>Chain: find_proxy -> check delay elapsed -> execute as delegator
    end

    D->>Chain: remove_proxy(delegate, proxy_type, delay)
    Note over Chain: Remove from Proxies[delegator]<br/>Unreserve deposit<br/>Does NOT clean Announcements
Loading

Pure Proxy Lifecycle

sequenceDiagram
    participant S as Spawner
    participant Chain as On-chain Storage
    participant Pure as Pure Account (no private key)

    S->>Chain: create_pure(proxy_type, delay, index)
    Note over Chain: Derive address: hash(spawner, height, ext_index, proxy_type, index)<br/>Proxies[pure] = [{delegate: spawner, proxy_type, delay}]<br/>Reserve deposit from spawner
    Chain-->>Pure: Pure account now exists

    Note over S: Control pure via proxy call
    S->>Chain: proxy(real=pure, call=any allowed call)
    Chain->>Chain: Execute call as pure account

    Note over S: To destroy (proxy_type MUST be Any):
    S->>Chain: proxy(real=pure, call=kill_pure(spawner, ...))
    Note over Chain: Verify caller is the pure account<br/>Remove Proxies[pure]<br/>Unreserve deposit to spawner
    Note over Pure: Account permanently inaccessible.<br/>Any remaining funds are lost forever.
Loading

Who Pays What

graph TB
    subgraph "Deposits -- reserved, returned on removal"
        PD["Proxy Deposit
        Paid by: Delegator
        When: add_proxy
        Returned: remove_proxy / remove_proxies"]

        PPD["Pure Proxy Deposit
        Paid by: Spawner
        When: create_pure
        Returned: kill_pure"]

        AD["Announcement Deposit
        Paid by: Delegate
        When: announce
        Returned: proxy_announced / remove_announcement / reject_announcement"]
    end

    subgraph "Transaction Fees"
        DF["Default: Delegate pays
        -- the account that signs the tx"]

        RF["set_real_pays_fee -- delegate, true:
        Delegator pays fees for
        this delegate proxy calls"]

        NF["proxy_announced:
        set_real_pays_fee does NOT apply.
        Signer always pays."]
    end
Loading

ProxyType Hierarchy (is_superset)

graph TD
    Any["Any
    All calls allowed.
    Only type that can call
    kill_pure and remove_proxies"]

    NonTransfer["NonTransfer"]
    NonCritical["NonCritical"]
    Staking["Staking"]
    Transfer["Transfer"]
    SmallTransfer["SmallTransfer"]
    Registration["Registration"]
    Owner["Owner"]
    ChildKeys["ChildKeys"]
    SwapHotkey["SwapHotkey"]
    RootClaim["RootClaim"]
    NonFungible["NonFungible"]
    SubnetLeaseBeneficiary["SubnetLeaseBeneficiary"]
    SudoUncheckedSetCode["SudoUncheckedSetCode"]

    Any -- "superset of" --> NonTransfer
    Any -- "superset of" --> NonCritical
    Any -- "superset of" --> Staking
    Any -- "superset of" --> Transfer
    Any -- "superset of" --> Registration
    Any -- "superset of" --> Owner
    Any -- "superset of" --> ChildKeys
    Any -- "superset of" --> SwapHotkey
    Any -- "superset of" --> RootClaim
    Any -- "superset of" --> NonFungible
    Any -- "superset of" --> SubnetLeaseBeneficiary
    Any -- "superset of" --> SudoUncheckedSetCode
    NonTransfer -- "superset of" --> Staking
    NonTransfer -- "superset of" --> Registration
    NonTransfer -- "superset of" --> Owner
    NonTransfer -- "superset of" --> NonCritical
    NonTransfer -- "superset of" --> ChildKeys
    NonTransfer -- "superset of" --> SwapHotkey
    NonTransfer -- "superset of" --> RootClaim
    NonTransfer -- "superset of" --> NonFungible
    NonTransfer -- "superset of" --> SubnetLeaseBeneficiary
    NonTransfer -- "superset of" --> SudoUncheckedSetCode
    NonTransfer -. "NOT superset of" .-> Transfer
    NonTransfer -. "NOT superset of" .-> SmallTransfer
    Transfer -- "superset of" --> SmallTransfer
Loading

Escalation Protection (do_proxy filter)

graph LR
    subgraph "Blocked by do_proxy"
        B1["add_proxy with broader type:
        BLOCKED if delegate proxy_type
        is NOT superset of the new type"]

        B2["remove_proxy with broader type:
        same rule as above"]

        B3["remove_proxies / kill_pure:
        BLOCKED unless proxy_type = Any"]
    end

    subgraph "Example"
        E1["Delegate has Staking proxy:
        -- cannot add_proxy with Any
        -- cannot add_proxy with Transfer
        -- can add_proxy with Staking"]
    end
Loading

Extrinsic Reference

Extrinsic Signed by Effect
add_proxy(delegate, type, delay) Delegator Add delegate
remove_proxy(delegate, type, delay) Delegator Remove specific delegate
remove_proxies() Delegator Remove all delegates
proxy(real, force_type, call) Delegate Immediate call as delegator (delay=0)
announce(real, call_hash) Delegate Announce future call (delay>0)
proxy_announced(delegate, real, type, call) Anyone Execute announced call after delay
reject_announcement(delegate, call_hash) Delegator Veto a pending announcement
remove_announcement(real, call_hash) Delegate Cancel own announcement
create_pure(type, delay, index) Spawner Create keyless account
kill_pure(spawner, type, index, height, ext_index) Pure (via proxy) Destroy pure account
set_real_pays_fee(delegate, pays_fee) Delegator Toggle fee payer for proxy calls

ProxyType Permissions Reference

Source: runtime/src/lib.rs L612-L828

Any

Filter: allow-list -- allows ALL calls.

The most permissive type. Required as the default by the pallet. Only proxy type that can execute kill_pure and remove_proxies through a proxy call.

NonTransfer (deny-list)

Allows: everything EXCEPT the following:

Blocked call
Balances::* (all balance transfer calls)
SubtensorModule::transfer_stake
SubtensorModule::schedule_swap_coldkey
SubtensorModule::swap_coldkey

NonCritical (deny-list)

Allows: everything EXCEPT the following:

Blocked call
SubtensorModule::dissolve_network
SubtensorModule::root_register
SubtensorModule::burned_register
Sudo::*

NonFungible (deny-list)

Allows: everything EXCEPT calls that move TAO/Alpha or change key ownership:

Blocked call
Balances::*
SubtensorModule::add_stake
SubtensorModule::add_stake_limit
SubtensorModule::remove_stake
SubtensorModule::remove_stake_limit
SubtensorModule::remove_stake_full_limit
SubtensorModule::unstake_all
SubtensorModule::unstake_all_alpha
SubtensorModule::swap_stake
SubtensorModule::swap_stake_limit
SubtensorModule::move_stake
SubtensorModule::transfer_stake
SubtensorModule::burned_register
SubtensorModule::root_register
SubtensorModule::schedule_swap_coldkey
SubtensorModule::swap_coldkey
SubtensorModule::swap_hotkey

Staking (allow-list)

Allows ONLY:

Allowed call
SubtensorModule::add_stake
SubtensorModule::remove_stake
SubtensorModule::unstake_all
SubtensorModule::unstake_all_alpha
SubtensorModule::swap_stake
SubtensorModule::swap_stake_limit
SubtensorModule::move_stake
SubtensorModule::add_stake_limit
SubtensorModule::remove_stake_limit
SubtensorModule::remove_stake_full_limit
SubtensorModule::set_root_claim_type

Note: transfer_stake is NOT included. A leaked Staking proxy cannot move funds out of the account.

Transfer (allow-list)

Allows ONLY:

Allowed call
Balances::transfer_keep_alive
Balances::transfer_allow_death
Balances::transfer_all
SubtensorModule::transfer_stake

SmallTransfer (allow-list, with amount limit)

Same calls as Transfer, but each call is checked against a per-call amount limit:

  • Balances::transfer_keep_alive / transfer_allow_death: value < 0.5 TAO (500,000,000 RAO)
  • SubtensorModule::transfer_stake: alpha_amount < 0.5 Alpha (500,000,000)

Any call exceeding the limit is rejected.

Owner (allow-list)

Allows ONLY:

Allowed call
AdminUtils::* (all admin utils calls EXCEPT sudo_set_sn_owner_hotkey)
SubtensorModule::set_subnet_identity
SubtensorModule::update_symbol

Registration (allow-list)

Allows ONLY:

Allowed call
SubtensorModule::burned_register
SubtensorModule::register

ChildKeys (allow-list)

Allows ONLY:

Allowed call
SubtensorModule::set_children
SubtensorModule::set_childkey_take

SwapHotkey (allow-list)

Allows ONLY:

Allowed call
SubtensorModule::swap_hotkey

RootClaim (allow-list)

Allows ONLY:

Allowed call
SubtensorModule::claim_root

SudoUncheckedSetCode (allow-list, nested check)

Allows ONLY:

Allowed call
Sudo::sudo_unchecked_weight wrapping System::set_code

Any other call inside sudo_unchecked_weight, or any call outside of it, is rejected.

SubnetLeaseBeneficiary (allow-list)

Allows ONLY:

Allowed call
SubtensorModule::start_call
AdminUtils::sudo_set_serving_rate_limit
AdminUtils::sudo_set_min_difficulty
AdminUtils::sudo_set_max_difficulty
AdminUtils::sudo_set_weights_version_key
AdminUtils::sudo_set_adjustment_alpha
AdminUtils::sudo_set_immunity_period
AdminUtils::sudo_set_min_allowed_weights
AdminUtils::sudo_set_kappa
AdminUtils::sudo_set_rho
AdminUtils::sudo_set_activity_cutoff
AdminUtils::sudo_set_network_registration_allowed
AdminUtils::sudo_set_network_pow_registration_allowed
AdminUtils::sudo_set_max_burn
AdminUtils::sudo_set_bonds_moving_average
AdminUtils::sudo_set_bonds_penalty
AdminUtils::sudo_set_commit_reveal_weights_enabled
AdminUtils::sudo_set_liquid_alpha_enabled
AdminUtils::sudo_set_alpha_values
AdminUtils::sudo_set_commit_reveal_weights_interval
AdminUtils::sudo_set_toggle_transfer

Deprecated (always reject)

Type Status
Triumvirate deprecated, filter returns false
Senate deprecated, filter returns false
Governance deprecated, filter returns false
RootWeights deprecated, filter returns false

Clone this wiki locally