Skip to content

The Proxy Pallet

Introduction

Proxy accounts can be set up to perform a limited number of actions on behalf of users and are useful for keeping the underlying accounts safe. They allow users to keep their primary account secured safely in cold storage while enabling the proxy to actively perform functions and participate in the network with the weight of the tokens in the primary account.

Substrate's proxy pallet enables you to create proxy accounts, remove proxy accounts, make calls as a proxy account, and announce proxy transactions. To add and remove proxy accounts, you can use the proxy precompile: a Solidity interface that can be interacted through the Ethereum API. For more information on how to use this contract, please refer to the Proxy Precompile guide.

This page will provide an overview of the extrinsics, storage methods, and getters for the pallet constants available in the proxy pallet.

Proxy Pallet Interface

Extrinsics

The proxy pallet provides the following extrinsics (functions):

addProxy(delegate, proxyType, delay) - registers a proxy account for the sender that is able to make calls on the sender's behalf. If delay is set to a value greater than 0, the proxy account will have to announce a transaction and wait that value of blocks before attempting to execute it as a proxy. Emits a ProxyAdded event
  • delegate - The account that will act as proxy (H160 format address, e.g., '0x123...'). This address will be able to submit transactions on behalf of the caller
  • proxyType - The permissions granted to the proxy account. Available options are:
    • Any: Allows all transactions
    • NonTransfer: Allows all transactions except balance transfers
    • Governance: Allows governance-related transactions
    • Staking: Allows staking-related transactions
    • CancelProxy: Only allows canceling other proxies
    • Balances: Allows balance transfers
    • AuthorMapping: Allows author mapping transactions
    • IdentityJudgement: Allows providing identity judgements
  • delay - Number of blocks that must pass after announcing a proxy transaction before it can be executed (u32). Set to 0 for immediate execution
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Initialize the keyring with ethereum type
  const keyring = new Keyring({ type: 'ethereum' });

  try {
    // Setup account from private key
    const PRIVATE_KEY = 'INSERT_PRIVATE_KEY';
    const account = keyring.addFromUri(PRIVATE_KEY);

    // Use an existing account as proxy
    const proxyAccount = 'INSERT_PROXY_ACCOUNT';

    // Define proxy parameters
    // Use the Staking variant from the ProxyType enum
    const proxyType = { Staking: null };
    const delay = 0; // No delay

    console.log('Validation checks:');
    console.log('Account address:', account.address);
    console.log('Proxy account address:', proxyAccount);
    console.log('Proxy type:', JSON.stringify(proxyType));
    console.log('Delay:', delay);

    // Create the addProxy transaction
    const tx = api.tx.proxy.addProxy(proxyAccount, proxyType, delay);

    // Sign and send the transaction
    await tx.signAndSend(account, ({ status, events }) => {
      if (status.isInBlock) {
        console.log(`Transaction included in block hash: ${status.asInBlock}`);

        // Process events
        events.forEach(({ event }) => {
          const { section, method, data } = event;
          console.log(`\t${section}.${method}:`, data.toString());

          // Handle any failures
          if (section === 'system' && method === 'ExtrinsicFailed') {
            const [dispatchError] = data;
            let errorInfo;

            if (dispatchError.isModule) {
              const decoded = api.registry.findMetaError(
                dispatchError.asModule
              );
              errorInfo = `${decoded.section}.${decoded.name}: ${decoded.docs}`;
            } else {
              errorInfo = dispatchError.toString();
            }
            console.error('Failure reason:', errorInfo);
          }

          // Log successful proxy addition
          if (section === 'proxy' && method === 'ProxyAdded') {
            console.log('Proxy successfully added!');
          }
        });

        process.exit(0);
      }
    });
  } catch (error) {
    console.error('Error in adding proxy:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});
Validation checks:
Account address: 0x3B939FeaD1557C741Ff06492FD0127bd287A421e
Proxy account address: 0x569BE8d8b04538318e1722f6e375FD381D2da865
Proxy type: {"Staking":null}
Delay: 0
Transaction included in block hash: 0xd9763b3eec3e50dfeec246f1537421a632ec5a3ab821a5e5e6b507c12930cd64
    balances.Withdraw: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3431276154061]
    balances.Reserved: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",2100000000000000]
    proxy.ProxyAdded: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e","0x569BE8d8b04538318e1722f6e375FD381D2da865","Staking",0]
Proxy successfully added!
    balances.Deposit: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",0]
    balances.Deposit: ["0x6d6F646c70632f74727372790000000000000000",686255230813]
    transactionPayment.TransactionFeePaid: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3431276154061,0]
    system.ExtrinsicSuccess: [{"weight":{"refTime":398219506,"proofSize":4310},"class":"Normal","paysFee":"Yes"}]
announce(real, callHash) - registers an announcement of a proxy transaction by proxy accounts that require a delay. Emits an Announced event
  • real - The account being proxied (H160 format address, e.g., '0x123...'). This is the account on whose behalf the delayed proxy intends to execute a call
  • callHash - The hash of the call that the proxy intends to execute after the delay period (32-byte hex string, e.g., '0x570ff355...'). This hash is derived from the actual call data that will be executed later
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Initialize the keyring with ethereum type
  const keyring = new Keyring({ type: 'ethereum' });

  try {
    // Setup accounts
    const PROXY_PRIVATE_KEY = 'INSERT_PROXY_PRIVATE_KEY';
    const proxyAccount = keyring.addFromUri(PROXY_PRIVATE_KEY);

    // The real account that the proxy will act on behalf of
    const realAccount = 'INSERT_REAL_ACCOUNT';

    // Use the provided call hash
    const callHash = 'INSERT_CALL_HASH';

    console.log('Validation checks:');
    console.log('Proxy account address:', proxyAccount.address);
    console.log('Real account address:', realAccount);
    console.log('Call hash:', callHash);

    // Create the announce transaction
    const tx = api.tx.proxy.announce(realAccount, callHash);

    // Sign and send the transaction
    await tx.signAndSend(proxyAccount, ({ status, events }) => {
      if (status.isInBlock) {
        console.log(`Transaction included in block hash: ${status.asInBlock}`);

        // Process events
        events.forEach(({ event }) => {
          const { section, method, data } = event;
          console.log(`\t${section}.${method}:`, data.toString());

          // Handle any failures
          if (section === 'system' && method === 'ExtrinsicFailed') {
            const [dispatchError] = data;
            let errorInfo;

            if (dispatchError.isModule) {
              const decoded = api.registry.findMetaError(
                dispatchError.asModule
              );
              errorInfo = `${decoded.section}.${decoded.name}: ${decoded.docs}`;
            } else {
              errorInfo = dispatchError.toString();
            }
            console.error('Failure reason:', errorInfo);
          }

          // Log successful announcement
          if (section === 'proxy' && method === 'Announced') {
            console.log('Proxy call successfully announced!');
            console.log(
              'You can execute the actual call after the delay period'
            );
          }
        });

        process.exit(0);
      }
    });
  } catch (error) {
    console.error('Error in announcing proxy call:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});
Validation checks:
Proxy account address: 0x569BE8d8b04538318e1722f6e375FD381D2da865
Real account address: 0x3B939FeaD1557C741Ff06492FD0127bd287A421e
Call hash: 0x570ff355e1471d3528cb4e2586bee7eafebc2efc89dd6f827188c69b15fff965
Transaction included in block hash: 0xdb5b9bb961ce3153387d2131911de218c08b8b09d8a625f36271ad98b2abf567
balances.Withdraw: ["0x569BE8d8b04538318e1722f6e375FD381D2da865",3682233905542]
balances.Reserved: ["0x569BE8d8b04538318e1722f6e375FD381D2da865","0x00000000000000000df77377c5f40000"]
proxy.Announced: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e","0x569BE8d8b04538318e1722f6e375FD381D2da865","0x570ff355e1471d3528cb4e2586bee7eafebc2efc89dd6f827188c69b15fff965"]
Proxy call successfully announced!
You can execute the actual call after the delay period
balances.Deposit: ["0x569BE8d8b04538318e1722f6e375FD381D2da865",0]
balances.Deposit: ["0x6d6F646c70632f74727372790000000000000000",736446781109]
transactionPayment.TransactionFeePaid: ["0x569BE8d8b04538318e1722f6e375FD381D2da865",3682233905542,0]
system.ExtrinsicSuccess: [{"weight":{"refTime":577384531,"proofSize":5302},"class":"Normal","paysFee":"Yes"}]
proxy(real, forceProxyType, call) - makes a transaction as a proxy. Emits a ProxyExecuted event
  • real - The account being proxied (H160 format address, e.g., '0x123...'). This is the account on whose behalf the proxy will execute the call
  • forceProxyType - The type of proxy right required to execute this call. Must match the proxy type that was specified when the proxy was added. Available options are:
    • Any: Allows all transactions
    • NonTransfer: Allows all transactions except balance transfers
    • Governance: Allows governance-related transactions
    • Staking: Allows staking-related transactions
    • CancelProxy: Only allows canceling other proxies
    • Balances: Allows balance transfers
    • AuthorMapping: Allows author mapping transactions
    • IdentityJudgement: Allows providing identity judgements
  • call - The actual call data to be executed by the proxy on behalf of the real account. This is the transaction that will be performed (e.g., a transfer, a stake, or any other valid runtime call)
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Initialize the keyring with ethereum type
  const keyring = new Keyring({ type: 'ethereum' });

  try {
    // Setup proxy account from private key
    const PROXY_PRIVATE_KEY = 'INSERT_PROXY_PRIVATE_KEY';
    const proxyAccount = keyring.addFromUri(PROXY_PRIVATE_KEY);

    // The real account that we're making the transaction for
    const realAccount = 'INSERT_REAL_ACCOUNT';

    // Destination account for the simple demo transfer
    const destinationAccount = 'INSERT_DESTINATION_ADDRESS';

    // Amount to transfer (1 DEV = 1e18 Wei)
    const transferAmount = '1000000000000000000'; // 1 DEV

    // Create the transfer call that we want to make via proxy
    const transferCall = api.tx.balances.transferAllowDeath(
      destinationAccount,
      transferAmount
    );

    // Create the proxy transaction
    // We'll specify Balances as the force proxy type since we're doing a transfer
    const tx = api.tx.proxy.proxy(
      realAccount,
      { Any: null }, // forceProxyType
      transferCall
    );

    console.log('Validation checks:');
    console.log('Proxy account:', proxyAccount.address);
    console.log('Real account:', realAccount);
    console.log('Destination account:', destinationAccount);
    console.log('Transfer amount:', transferAmount, 'Wei (1 DEV)');

    // Sign and send the transaction
    await tx.signAndSend(proxyAccount, ({ status, events }) => {
      if (status.isInBlock) {
        console.log(`Transaction included in block hash: ${status.asInBlock}`);

        // Process events
        events.forEach(({ event }) => {
          const { section, method, data } = event;
          console.log(`\t${section}.${method}:`, data.toString());

          // Handle any failures
          if (section === 'system' && method === 'ExtrinsicFailed') {
            const [dispatchError] = data;
            let errorInfo;

            if (dispatchError.isModule) {
              const decoded = api.registry.findMetaError(
                dispatchError.asModule
              );
              errorInfo = `${decoded.section}.${decoded.name}: ${decoded.docs}`;
            } else {
              errorInfo = dispatchError.toString();
            }
            console.error('Failure reason:', errorInfo);
          }

          // Log successful transfer
          if (section === 'balances' && method === 'Transfer') {
            console.log('\nTransfer successfully executed via proxy!');
            const [from, to, amount] = data;
            console.log('From:', from.toString());
            console.log('To:', to.toString());
            console.log('Amount:', amount.toString());
          }
        });

        process.exit(0);
      }
    });
  } catch (error) {
    console.error('Error in proxy transaction:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});
Validation checks:
Proxy account: 0x3B939FeaD1557C741Ff06492FD0127bd287A421e
Real account: 0x569BE8d8b04538318e1722f6e375FD381D2da865
Destination account: 0x8c9c5F11d162a69E979F2DB9047A862ecbcA23Cb
Transfer amount: 1000000000000000000 Wei (1 DEV)
Force proxy type: Balances
Transaction included in block hash: 0xc347d714324e795c0e27ef574c8f924d7a52935314044cf2e2a395bc32ef5070
    balances.Withdraw: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3817444390449]
    balances.Transfer: ["0x569BE8d8b04538318e1722f6e375FD381D2da865","0x8c9c5F11d162a69E979F2DB9047A862ecbcA23Cb","0x00000000000000000de0b6b3a7640000"]

Transfer successfully executed via proxy!
From: 0x569BE8d8b04538318e1722f6e375FD381D2da865
To: 0x8c9c5F11d162a69E979F2DB9047A862ecbcA23Cb
Amount: 1000000000000000000
    proxy.ProxyExecuted: [{"ok":null}]
    balances.Deposit: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",0]
    balances.Deposit: ["0x6d6F646c70632f74727372790000000000000000",763488878090]
    transactionPayment.TransactionFeePaid: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3817444390449,0]
    system.ExtrinsicSuccess: [{"weight":{"refTime":684752866,"proofSize":8691},"class":"Normal","paysFee":"Yes"}]
proxyAnnounced(delegate, real, forceProxyType, call) - makes a transaction as a proxy and removes previous corresponding announcements. Emits a ProxyExecuted event
  • delegate - The account that previously made the announcement (H160 format address, e.g., '0x123...'). This must match the proxy account that called the announce function
  • real - The account being proxied (H160 format address, e.g., '0x123...'). This is the account on whose behalf the proxy will execute the call
  • forceProxyType - The type of proxy right required to execute this call. Must match the proxy type that was specified when the proxy was added. Available options are:
    • Any: Allows all transactions
    • NonTransfer: Allows all transactions except balance transfers
    • Governance: Allows governance-related transactions
    • Staking: Allows staking-related transactions
    • CancelProxy: Only allows canceling other proxies
    • Balances: Allows balance transfers
    • AuthorMapping: Allows author mapping transactions
    • IdentityJudgement: Allows providing identity judgements
  • call - The actual call to be executed (must match the call that was previously announced). This is the transaction that will be performed (e.g., a transfer, a stake, or any other valid runtime call)
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Initialize the keyring with ethereum type
  const keyring = new Keyring({ type: 'ethereum' });

  try {
    // Setup proxy account from private key (this is the account executing the call)
    const PROXY_PRIVATE_KEY = 'INSERT_PROXY_PRIVATE_KEY';
    const proxyAccount = keyring.addFromUri(PROXY_PRIVATE_KEY);

    // The account that delegated proxy rights
    const realAccount = 'INSERT_REAL_ACCOUNT';

    // The delegate account (the proxy account that made the announcement)
    const delegateAccount = proxyAccount.address;

    // Destination account for the transfer
    const destinationAccount = 'INSERT_DESTINATION_ADDRESS';

    // Amount to transfer (1 DEV = 1e18 Wei)
    const transferAmount = '1000000000000000000'; // 1 DEV

    // Create the transfer call that was previously announced
    const transferCall = api.tx.balances.transferAllowDeath(
      destinationAccount,
      transferAmount
    );

    // Create the proxyAnnounced transaction
    const tx = api.tx.proxy.proxyAnnounced(
      delegateAccount,
      realAccount,
      { Balances: null }, // forceProxyType
      transferCall
    );

    console.log('Validation checks:');
    console.log('Delegate (Proxy) account:', delegateAccount);
    console.log('Real account:', realAccount);
    console.log('Force proxy type: Balances');
    console.log('Call details:');
    console.log('- Destination:', destinationAccount);
    console.log('- Amount:', transferAmount, 'Wei (1 DEV)');

    // Sign and send the transaction
    await tx.signAndSend(proxyAccount, ({ status, events }) => {
      if (status.isInBlock) {
        console.log(`Transaction included in block hash: ${status.asInBlock}`);

        // Process events
        events.forEach(({ event }) => {
          const { section, method, data } = event;
          console.log(`\t${section}.${method}:`, data.toString());

          // Handle any failures
          if (section === 'system' && method === 'ExtrinsicFailed') {
            const [dispatchError] = data;
            let errorInfo;

            if (dispatchError.isModule) {
              const decoded = api.registry.findMetaError(
                dispatchError.asModule
              );
              errorInfo = `${decoded.section}.${decoded.name}: ${decoded.docs}`;
            } else {
              errorInfo = dispatchError.toString();
            }
            console.error('Failure reason:', errorInfo);
          }

          // Log successful proxy execution
          if (section === 'proxy' && method === 'ProxyExecuted') {
            console.log('\nProxy call successfully executed!');
          }

          // Log successful transfer
          if (section === 'balances' && method === 'Transfer') {
            const [from, to, amount] = data;
            console.log('Transfer details:');
            console.log('From:', from.toString());
            console.log('To:', to.toString());
            console.log('Amount:', amount.toString());
          }
        });

        process.exit(0);
      }
    });
  } catch (error) {
    console.error('Error in proxy announced transaction:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});
    Validation checks:
    Proxy account: 0x3B939FeaD1557C741Ff06492FD0127bd287A421e
    Real account: 0x569BE8d8b04538318e1722f6e375FD381D2da865
    Destination account: 0x8c9c5F11d162a69E979F2DB9047A862ecbcA23Cb
    Transfer amount: 1000000000000000000 Wei (1 DEV)
    Force proxy type: Balances
    Transaction included in block hash: 0xc347d714324e795c0e27ef574c8f924d7a52935314044cf2e2a395bc32ef5070
        balances.Withdraw: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3817444390449]
        balances.Transfer: ["0x569BE8d8b04538318e1722f6e375FD381D2da865","0x8c9c5F11d162a69E979F2DB9047A862ecbcA23Cb","0x00000000000000000de0b6b3a7640000"]

    Transfer successfully executed via proxy!
    From: 0x569BE8d8b04538318e1722f6e375FD381D2da865
    To: 0x8c9c5F11d162a69E979F2DB9047A862ecbcA23Cb
    Amount: 1000000000000000000
        proxy.ProxyExecuted: [{"ok":null}]
        balances.Deposit: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",0]
        balances.Deposit: ["0x6d6F646c70632f74727372790000000000000000",763488878090]
        transactionPayment.TransactionFeePaid: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3817444390449,0]
        system.ExtrinsicSuccess: [{"weight":{"refTime":684752866,"proofSize":8691},"class":"Normal","paysFee":"Yes"}]
rejectAnnouncement(delegate, callHash) - if the sender is a prime account, this removes a specific announcement from their proxy account
  • delegate - The account that previously made the announcement (H160 format address, e.g., '0x123...'). This must match the proxy account that called the announce function
  • callHash - The hash call to be executed (must match the call that was previously announced)
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Initialize the keyring with ethereum type
  const keyring = new Keyring({ type: 'ethereum' });

  try {
    // Setup the real account (the one that will reject the announcement)
    const REAL_PRIVATE_KEY = 'INSERT_PRIVATE_KEY';
    const realAccount = keyring.addFromUri(REAL_PRIVATE_KEY);

    // The proxy account that made the announcement
    const delegateAccount = 'INSERT_PROXY_ACCOUNT';

    // The call hash of the announcement to reject
    const callHash = 'INSERT_CALL_HASH';

    console.log('Validation checks:');
    console.log('Real account (rejector):', realAccount.address);
    console.log('Delegate account to reject:', delegateAccount);
    console.log('Call hash to reject:', callHash);

    // Create the reject announcement transaction
    const tx = api.tx.proxy.rejectAnnouncement(delegateAccount, callHash);

    // Sign and send the transaction
    await tx.signAndSend(realAccount, ({ status, events }) => {
      if (status.isInBlock) {
        console.log(`Transaction included in block hash: ${status.asInBlock}`);

        // Process events
        events.forEach(({ event }) => {
          const { section, method, data } = event;
          console.log(`\t${section}.${method}:`, data.toString());

          // Handle any failures
          if (section === 'system' && method === 'ExtrinsicFailed') {
            const [dispatchError] = data;
            let errorInfo;

            if (dispatchError.isModule) {
              const decoded = api.registry.findMetaError(
                dispatchError.asModule
              );
              errorInfo = `${decoded.section}.${decoded.name}: ${decoded.docs}`;
            } else {
              errorInfo = dispatchError.toString();
            }
            console.error('Failure reason:', errorInfo);
          }

          // Log successful rejection
          if (section === 'proxy' && method === 'AnnouncementRejected') {
            console.log('\nAnnouncement successfully rejected!');
            const [accountId, hash] = data;
            console.log('Rejected delegate:', accountId.toString());
            console.log('Rejected call hash:', hash.toString());
          }
        });

        process.exit(0);
      }
    });
  } catch (error) {
    console.error('Error in rejecting announcement:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});

Validation checks: Real account (rejector): 0x569BE8d8b04538318e1722f6e375FD381D2da865 Delegate account to reject: 0x569BE8d8b04538318e1722f6e375FD381D2da865 Call hash to reject: 0xaf2dd398c8ee31d963d1f24764b8857e27314b3e937385c3ff60c034a36e925c Transaction included in block hash: 0x76073a7b5eae1b9efb4a8142916fb33fa9f11a31f9e1f231ecb1ebd1af7a2a47 balances.Withdraw: ["0x569BE8d8b04538318e1722f6e375FD381D2da865",3621382860542] balances.Deposit: ["0x569BE8d8b04538318e1722f6e375FD381D2da865",0] balances.Deposit: ["0x6d6F646c70632f74727372790000000000000000",724276572109] transactionPayment.TransactionFeePaid: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3621382860542,0] ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3817444390449,0] system.ExtrinsicSuccess: [{"weight":{"refTime":684752866,"proofSize":8691},"class":"Normal","paysFee":"Yes"}]

removeAnnouncement(real, callHash) - if the sender is a proxy account, this removes a specific announcement to their prime account
  • real - The account that was designated as the real account in the original announcement (H160 format address, e.g., '0x123...'). This is the account on whose behalf the proxy had announced a future transaction
  • callHash - The hash of the call from the original announcement (32-byte hex string, e.g., '0x570ff355...'). This uniquely identifies which announced transaction should be removed
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Initialize the keyring with ethereum type
  const keyring = new Keyring({ type: 'ethereum' });

  try {
    // Setup the proxy account (the one that will remove its own announcement)
    const PROXY_PRIVATE_KEY = 'INSERT_PROXY_PRIVATE_KEY';
    const proxyAccount = keyring.addFromUri(PROXY_PRIVATE_KEY);

    // The real account that the announcement was made for
    const realAccount = 'INSERT_REAL_ACCOUNT';

    // The call hash of the announcement to remove
    const callHash = 'INSERT_CALL_HASH';

    console.log('Validation checks:');
    console.log('Proxy account (remover):', proxyAccount.address);
    console.log('Real account:', realAccount);
    console.log('Call hash to remove:', callHash);

    // Create the remove announcement transaction
    const tx = api.tx.proxy.removeAnnouncement(realAccount, callHash);

    // Sign and send the transaction
    await tx.signAndSend(proxyAccount, ({ status, events }) => {
      if (status.isInBlock) {
        console.log(`Transaction included in block hash: ${status.asInBlock}`);

        // Process events
        events.forEach(({ event }) => {
          const { section, method, data } = event;
          console.log(`\t${section}.${method}:`, data.toString());

          // Handle any failures
          if (section === 'system' && method === 'ExtrinsicFailed') {
            const [dispatchError] = data;
            let errorInfo;

            if (dispatchError.isModule) {
              const decoded = api.registry.findMetaError(
                dispatchError.asModule
              );
              errorInfo = `${decoded.section}.${decoded.name}: ${decoded.docs}`;
            } else {
              errorInfo = dispatchError.toString();
            }
            console.error('Failure reason:', errorInfo);
          }

          // Log successful announcement removal
          if (section === 'proxy' && method === 'AnnouncementRejected') {
            console.log('\nAnnouncement successfully removed!');
            const [accountId, hash] = data;
            console.log('Removed for real account:', accountId.toString());
            console.log('Removed call hash:', hash.toString());
          }
        });

        process.exit(0);
      }
    });
  } catch (error) {
    console.error('Error in removing announcement:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});
Validation checks:
Proxy account (remover): 0x3B939FeaD1557C741Ff06492FD0127bd287A421e
Real account: 0x569BE8d8b04538318e1722f6e375FD381D2da865
Call hash to remove: 0x570ff355e1471d3528cb4e2586bee7eafebc2efc89dd6f827188c69b15fff965
Transaction included in block hash: 0x767724a583d93b558c56f2e241d2334bf91773269ceb1e0a60435f7cbbe2205a
    balances.Withdraw: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3621330308042]
    balances.Deposit: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",0]
    balances.Deposit: ["0x6d6F646c70632f74727372790000000000000000",724266061609]
    transactionPayment.TransactionFeePaid: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3621330308042,0]
    ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3817444390449,0]
        system.ExtrinsicSuccess: [{"weight":{"refTime":684752866,"proofSize":8691},"class":"Normal","paysFee":"Yes"}]
removeProxies() - unregisters all proxy accounts for the sender

None

import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Initialize the keyring with ethereum type
  const keyring = new Keyring({ type: 'ethereum' });

  try {
    // Setup the account that wants to remove all its proxies
    const PRIVATE_KEY = 'INSERT_PRIVATE_KEY';
    const account = keyring.addFromUri(PRIVATE_KEY);

    console.log('Validation checks:');
    console.log('Account removing all proxies:', account.address);

    // Optional: Query existing proxies before removal
    const proxies = await api.query.proxy.proxies(account.address);
    console.log('\nCurrent proxies before removal:', proxies.toHuman());

    // Create the removeProxies transaction
    const tx = api.tx.proxy.removeProxies();

    // Sign and send the transaction
    await tx.signAndSend(account, ({ status, events }) => {
      if (status.isInBlock) {
        console.log(
          `\nTransaction included in block hash: ${status.asInBlock}`
        );

        // Process events
        events.forEach(({ event }) => {
          const { section, method, data } = event;
          console.log(`\t${section}.${method}:`, data.toString());

          // Handle any failures
          if (section === 'system' && method === 'ExtrinsicFailed') {
            const [dispatchError] = data;
            let errorInfo;

            if (dispatchError.isModule) {
              const decoded = api.registry.findMetaError(
                dispatchError.asModule
              );
              errorInfo = `${decoded.section}.${decoded.name}: ${decoded.docs}`;
            } else {
              errorInfo = dispatchError.toString();
            }
            console.error('Failure reason:', errorInfo);
          }

          // Log successful proxy removals
          if (section === 'proxy' && method === 'ProxyRemoved') {
            console.log('\nProxy successfully removed!');
            const [delegator, delegate, proxyType, delay] = data;
            console.log('Delegator:', delegator.toString());
            console.log('Removed delegate:', delegate.toString());
            console.log('Proxy type:', proxyType.toString());
            console.log('Delay:', delay.toString());
          }
        });

        // Optional: Query proxies after removal to confirm
        api.query.proxy.proxies(account.address).then((afterProxies) => {
          console.log('\nProxies after removal:', afterProxies.toHuman());
          process.exit(0);
        });
      }
    });
  } catch (error) {
    console.error('Error in removing all proxies:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});
Validation checks:
Account removing all proxies: 0x3B939FeaD1557C741Ff06492FD0127bd287A421e

Current proxies before removal: [
  [
    {
      delegate: '0x0000000000000000000000000000000000000000',
      proxyType: 'Governance',
      delay: '0'
    },
    {
      delegate: '0x4b8C667590E6a28497Ea4be5FACB7e9869A64EAE',
      proxyType: 'Staking',
      delay: '0'
    },
    {
      delegate: '0x569BE8d8b04538318e1722f6e375FD381D2da865',
      proxyType: 'Staking',
      delay: '0'
    },
    {
      delegate: '0x569BE8d8b04538318e1722f6e375FD381D2da865',
      proxyType: 'Balances',
      delay: '100'
    }
  ],
  '1,009,200,000,000,000,000'
]

Transaction included in block hash: 0x2ef80fe655c98f47ba82cc2ee7937e03d2c6211195dc03ef02e6d47fbbdcd944
    balances.Withdraw: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3403192090986]
    balances.Unreserved: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e","0x00000000000000000e01660d93530000"]
    balances.Deposit: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",0]
    balances.Deposit: ["0x6d6F646c70632f74727372790000000000000000",680638418198]
    transactionPayment.TransactionFeePaid: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3403192090986,0]
    system.ExtrinsicSuccess: [{"weight":{"refTime":395752965,"proofSize":4310},"class":"Normal","paysFee":"Yes"}]

Proxies after removal: [ [], '0' ]
removeProxy(delegate, proxyType, delay) - unregisters a specific proxy account for the sender. Emits a ProxyRemoved event
  • delegate - The proxy account to remove (H160 format address, e.g., '0x123...'). This must be an existing proxy account that was previously registered using addProxy
  • proxyType - The type of proxy to remove. Must match exactly what was set when the proxy was added. Available options are:
    • Any: Allows all transactions
    • NonTransfer: Allows all transactions except balance transfers
    • Governance: Allows governance-related transactions
    • Staking: Allows staking-related transactions
    • CancelProxy: Only allows canceling other proxies
    • Balances: Allows balance transfers
    • AuthorMapping: Allows author mapping transactions
    • IdentityJudgement: Allows providing identity judgements
  • delay - The announcement delay in blocks that was set when adding the proxy (u32). Must match exactly what was set when the proxy was added (e.g., if proxy was added with delay=100, must use delay=100 to remove it)
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Initialize the keyring with ethereum type
  const keyring = new Keyring({ type: 'ethereum' });

  try {
    // Setup the account that wants to remove a specific proxy
    const PRIVATE_KEY = 'INSERT_PRIVATE_KEY';
    const account = keyring.addFromUri(PRIVATE_KEY);

    // The proxy account to remove
    const proxyToRemove = 'INSERT_PROXY_ACCOUNT';

    // Must match the original proxy type and delay that was set when adding the proxy
    const proxyType = { Any: null };
    const delay = 0;

    console.log('Validation checks:');
    console.log('Account removing proxy:', account.address);
    console.log('Proxy being removed:', proxyToRemove);
    console.log('Proxy type:', JSON.stringify(proxyType));
    console.log('Delay:', delay);

    // Optional: Query existing proxies before removal
    const proxiesBefore = await api.query.proxy.proxies(account.address);
    console.log('\nCurrent proxies before removal:', proxiesBefore.toHuman());

    // Create the removeProxy transaction
    const tx = api.tx.proxy.removeProxy(proxyToRemove, proxyType, delay);

    // Sign and send the transaction
    await tx.signAndSend(account, ({ status, events }) => {
      if (status.isInBlock) {
        console.log(
          `\nTransaction included in block hash: ${status.asInBlock}`
        );

        // Process events
        events.forEach(({ event }) => {
          const { section, method, data } = event;
          console.log(`\t${section}.${method}:`, data.toString());

          // Handle any failures
          if (section === 'system' && method === 'ExtrinsicFailed') {
            const [dispatchError] = data;
            let errorInfo;

            if (dispatchError.isModule) {
              const decoded = api.registry.findMetaError(
                dispatchError.asModule
              );
              errorInfo = `${decoded.section}.${decoded.name}: ${decoded.docs}`;
            } else {
              errorInfo = dispatchError.toString();
            }
            console.error('Failure reason:', errorInfo);
          }

          // Log successful proxy removal
          if (section === 'proxy' && method === 'ProxyRemoved') {
            console.log('\nProxy successfully removed!');
            const [delegator, delegate, proxyType, delay] = data;
            console.log('Delegator:', delegator.toString());
            console.log('Removed delegate:', delegate.toString());
            console.log('Proxy type:', proxyType.toString());
            console.log('Delay:', delay.toString());
          }
        });

        // Optional: Query proxies after removal to confirm
        api.query.proxy.proxies(account.address).then((afterProxies) => {
          console.log('\nProxies after removal:', afterProxies.toHuman());
          process.exit(0);
        });
      }
    });
  } catch (error) {
    console.error('Error in removing proxy:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});
Validation checks:
Account removing proxy: 0x3B939FeaD1557C741Ff06492FD0127bd287A421e
Proxy being removed: 0x569BE8d8b04538318e1722f6e375FD381D2da865
Proxy type: {"Any":null}
Delay: 0

Current proxies before removal: [
  [
    {
      delegate: '0x569BE8d8b04538318e1722f6e375FD381D2da865',
      proxyType: 'Any',
      delay: '0'
    }
  ],
  '1,002,900,000,000,000,000'
]

Transaction included in block hash: 0x8402c11ca656798ad54eea16c5c05b5fefa5d5d23beb590d214d2fa4168d8af9
    balances.Withdraw: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3431367169061]
    balances.Unreserved: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e","0x00000000000000000deb043c853d4000"]
    proxy.ProxyRemoved: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e","0x569BE8d8b04538318e1722f6e375FD381D2da865","Any",0]

Proxy successfully removed!
Delegator: 0x3B939FeaD1557C741Ff06492FD0127bd287A421e
Removed delegate: 0x569BE8d8b04538318e1722f6e375FD381D2da865
Proxy type: Any
Delay: 0
    balances.Deposit: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",0]
    balances.Deposit: ["0x6d6F646c70632f74727372790000000000000000",686273433813]
    transactionPayment.TransactionFeePaid: ["0x3B939FeaD1557C741Ff06492FD0127bd287A421e",3431367169061,0]
    system.ExtrinsicSuccess: [{"weight":{"refTime":398292318,"proofSize":4310},"class":"Normal","paysFee":"Yes"}]

Proxies after removal: [ [], '0' ]

Note

Anonymous proxies are disabled on Moonbeam networks because they are easy to misuse. Incorrect usage can cause a permanent loss of funds and unreserved balances.

Storage Methods

The proxy pallet includes the following read-only storage methods to obtain chain state data:

announcements(AccountId20) - returns all announcements made by the specified proxy account
  • AccountId20 - The proxy account's address in H160 format (e.g., '0x123...') whose announcements you want to query

Returns a tuple containing: - Array of announcements, each containing: - real: AccountId20 (The account the announcement was made for) - callHash: H256 (The hash of the announced call) - height: BlockNumber (The block number when announced) - Balance (The amount reserved to place the announcements)

import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  try {
    // Initialize connection to the network
    const api = await ApiPromise.create({
      provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
    });

    // The proxy address to query
    const proxyAddress = 'INSERT_PROXY_ACCOUNT';

    // Query announcements
    const announcements = await api.query.proxy.announcements(proxyAddress);

    // Log the results
    console.log('Querying announcements for proxy:', proxyAddress);
    console.log(
      '\nAnnouncements:',
      JSON.stringify(announcements.toHuman(), null, 2)
    );

    process.exit(0);
  } catch (error) {
    console.error('Error occurred:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});
palletVersion() - returns the current pallet version
  • None

Returns a single number representing the current version of the proxy pallet

import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  try {
    // Initialize connection to the network
    const api = await ApiPromise.create({
      provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
    });

    // Query pallet version
    const version = await api.query.proxy.palletVersion();

    // Log the result
    console.log('Proxy Pallet Version:', version.toHuman());

    process.exit(0);
  } catch (error) {
    console.error('Error occurred:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});
proxies(AccountId20) - returns a map and count of all proxy accounts for a specified primary account
  • AccountId20 - The primary account's address in H160 format (e.g., '0x123...') whose proxies you want to query

Returns a tuple containing: - Array of ProxyDefinition, each containing: - delegate: AccountId20 (The proxy account address) - proxyType: Enum (The type of proxy) - delay: Number (The announcement delay in blocks) - Balance (The amount reserved to place the proxies)

import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  try {
    // Initialize connection to the network
    const api = await ApiPromise.create({
      provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
    });

    // The account address to query proxies for
    const accountAddress = 'INSERT_REAL_ACCOUNT';

    // Query proxies
    const [proxies, deposit] = await api.query.proxy.proxies(accountAddress);

    // Log the results with formatted JSON
    console.log('Querying proxies for account:', accountAddress);
    console.log('\nProxies:', JSON.stringify(proxies.toHuman(), null, 2));
    console.log('Required deposit:', deposit.toHuman());

    // Display in a more readable format
    console.log('\nProxy Details:');
    proxies.forEach((proxy, index) => {
      console.log(`\nProxy #${index + 1}:`);
      console.log('  Delegate:', proxy.delegate.toString());
      console.log('  Proxy Type:', proxy.proxyType.toString());
      console.log('  Delay:', proxy.delay.toString());
    });

    process.exit(0);
  } catch (error) {
    console.error('Error occurred:', error);
    process.exit(1);
  }
};

// Execute the script
main().catch((error) => {
  console.error('Script error:', error);
  process.exit(1);
});

Pallet Constants

The proxy pallet includes the following read-only functions to obtain pallet constants:

announcementDepositBase() - returns the base amount of currency needed to reserve for creating an announcement
  • None

Returns a Balance value representing the base deposit amount in Wei required for creating a proxy announcement

import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Query the base deposit
  const baseDeposit = await api.consts.proxy.announcementDepositBase;

  console.log('Announcement Base Deposit:', baseDeposit.toHuman());

  process.exit(0);
};

main().catch(console.error);
announcementDepositFactor() - returns the amount of currency needed per announcement made

None

Returns a Balance value representing the additional deposit amount in Wei required for each announcement made

import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Query the deposit factor
  const depositFactor = await api.consts.proxy.announcementDepositFactor;

  console.log('Announcement Deposit Factor:', depositFactor.toHuman());

  process.exit(0);
};

main().catch(console.error);
maxPending() - returns the maximum amount of time-delayed announcements that are allowed to be pending

None

Returns a u32 value representing the maximum number of announcements that can be pending for a proxy account

import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Query max pending announcements
  const maxPending = await api.consts.proxy.maxPending;

  console.log('Maximum Pending Announcements:', maxPending.toHuman());

  process.exit(0);
};

main().catch(console.error);
maxProxies() - returns the maximum amount of proxies allowed for a single account

None

Returns a u32 value representing the maximum number of proxy accounts that can be registered to a single account

 import { ApiPromise, WsProvider } from '@polkadot/api';

 const main = async () => {
   // Initialize the API
   const api = await ApiPromise.create({
     provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
   });

   // Query max proxies allowed
   const maxProxies = await api.consts.proxy.maxProxies;

   console.log('Maximum Proxies per Account:', maxProxies.toHuman());

   process.exit(0);
 };

 main().catch(console.error);
proxyDepositBase() - returns the base amount of currency needed to reserve for creating a proxy

None

Returns a Balance value representing the base deposit amount in Wei required for creating a proxy

import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Query the base deposit for proxy creation
  const baseDeposit = await api.consts.proxy.proxyDepositBase;

  console.log('Proxy Base Deposit:', baseDeposit.toHuman());

  process.exit(0);
};

main().catch(console.error);
proxyDepositFactor() - returns the amount of currency needed per proxy added

None

Returns a Balance value representing the additional deposit amount in Wei required for each proxy registered

import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  // Initialize the API
  const api = await ApiPromise.create({
    provider: new WsProvider('wss://moonbase-alpha.public.blastapi.io'),
  });

  // Query the deposit factor for proxies
  const depositFactor = await api.consts.proxy.proxyDepositFactor;

  console.log('Proxy Deposit Factor:', depositFactor.toHuman());

  process.exit(0);
};

main().catch(console.error);
Last update: November 22, 2024
| Created: August 12, 2022