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 callerproxyType
- The permissions granted to the proxy account. Available options are:Any
: Allows all transactionsNonTransfer
: Allows all transactions except balance transfersGovernance
: Allows governance-related transactionsStaking
: Allows staking-related transactionsCancelProxy
: Only allows canceling other proxiesBalances
: Allows balance transfersAuthorMapping
: Allows author mapping transactionsIdentityJudgement
: Allows providing identity judgements
delay
- Number of blocks that must pass after announcing a proxy transaction before it can be executed (u32). Set to0
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 callcallHash
- 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 callforceProxyType
- 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 transactionsNonTransfer
: Allows all transactions except balance transfersGovernance
: Allows governance-related transactionsStaking
: Allows staking-related transactionsCancelProxy
: Only allows canceling other proxiesBalances
: Allows balance transfersAuthorMapping
: Allows author mapping transactionsIdentityJudgement
: 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 functionreal
- The account being proxied (H160 format address, e.g., '0x123...'). This is the account on whose behalf the proxy will execute the callforceProxyType
- 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 transactionsNonTransfer
: Allows all transactions except balance transfersGovernance
: Allows governance-related transactionsStaking
: Allows staking-related transactionsCancelProxy
: Only allows canceling other proxiesBalances
: Allows balance transfersAuthorMapping
: Allows author mapping transactionsIdentityJudgement
: 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 functioncallHash
- 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 transactioncallHash
- 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 addProxyproxyType
- The type of proxy to remove. Must match exactly what was set when the proxy was added. Available options are:Any
: Allows all transactionsNonTransfer
: Allows all transactions except balance transfersGovernance
: Allows governance-related transactionsStaking
: Allows staking-related transactionsCancelProxy
: Only allows canceling other proxiesBalances
: Allows balance transfersAuthorMapping
: Allows author mapping transactionsIdentityJudgement
: 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);
| Created: August 12, 2022