Interacting with the Proxy Precompile¶
Introduction¶
The Proxy Precompile allows accounts to set proxy accounts via the Ethereum API. Proxy accounts can perform limited actions on behalf of the proxied account, such as governance, balance transfers, management or privileged transactions, and others.
If a user wanted to provide another user access to a limited number of actions on their behalf, traditionally, the only method to do so would be to share that account's private key. However, Tanssi EVM appchains include the proxy module, providing an additional layer of security. With proxies, many accounts can perform actions for a primary account, and such permissions can be revoked at any time. This is best if, for example, a user wants to keep their wallet safe in cold storage but still wants to access parts of the wallet's functionality, like governance or staking.
Note
The Proxy Precompile can only be called from an Externally Owned Account (EOA) or by the Batch Precompile.
To learn more about proxy accounts and how to set them up for your own purposes without use of the Proxy Precompile, visit the Proxy Accounts page.
The Proxy Precompile is located at the following address:
0x0000000000000000000000000000000000000805
Note
There can be some unintended consequences when using precompiles. Tanssi's precompiles are derived from Moonbeam's, and as such, please familiarize yourself with Moonbeam's Precompile Security Considerations.
Prerequisites¶
Tto follow along with the contents in this guide, you'll need:
- Access to a Tanssi EVM appchain running runtime 700 or above
- An EVM-compatible wallet configured to work with your appchain. You can also connect your wallet to the demo EVM appchain
- An account with enough funds to pay the required fees and deposits
- A second account that you control to use as a proxy
The Proxy Solidity Interface¶
Proxy.sol
is an interface that allows developers to interact with the precompile's functions.
Proxy.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.3;
/// @author The Moonsong Labs Team
/// @title Pallet Proxy Interface
/// @title The interface through which solidity contracts will interact with the Proxy pallet
interface Proxy {
/// @dev Defines the proxy permission types.
/// The values start at `0` (most permissive) and are represented as `uint8`
enum ProxyType {
Any,
NonTransfer,
Governance,
Staking,
CancelProxy,
Balances,
AuthorMapping,
IdentityJudgement
}
/// @dev Register a proxy account for the sender that is able to make calls on its behalf
/// @custom:selector 74a34dd3
/// @param delegate The account that the caller would like to make a proxy
/// @param proxyType The permissions allowed for this proxy account
/// @param delay The announcement period required of the initial proxy, will generally be zero
function addProxy(
address delegate,
ProxyType proxyType,
uint32 delay
) external;
/// @dev Removes a proxy account from the sender
/// @custom:selector fef3f708
/// @param delegate The account that the caller would like to remove as a proxy
/// @param proxyType The permissions currently enabled for the removed proxy account
/// @param delay The announcement period required of the initial proxy, will generally be zero
function removeProxy(
address delegate,
ProxyType proxyType,
uint32 delay
) external;
/// @dev Unregister all proxy accounts for the sender
/// @custom:selector 14a5b5fa
function removeProxies() external;
/// @dev Dispatch the given subcall (`callTo`, `callData`) from an account that the sender
/// is authorised for through `addProxy`
/// @custom:selector 0d3cff86
/// @param real The account that the proxy will make a call on behalf of
/// @param callTo Recipient of the call to be made by the `real` account
/// @param callData Data of the call to be made by the `real` account
function proxy(
address real,
address callTo,
bytes memory callData
) external payable;
/// @dev Dispatch the given subcall (`callTo`, `callData`) from an account that the sender
/// is authorised for through `addProxy`
/// @custom:selector 685b9d2f
/// @param real The account that the proxy will make a call on behalf of
/// @param forceProxyType Specify the exact proxy type to be used and checked for this call
/// @param callTo Recipient of the call to be made by the `real` account
/// @param callData Data of the call to be made by the `real` account
function proxyForceType(
address real,
ProxyType forceProxyType,
address callTo,
bytes memory callData
) external payable;
/// @dev Checks if the caller has an account proxied with a given proxy type
/// @custom:selector e26d38ed
/// @param real The real account that maybe has a proxy
/// @param delegate The account that the caller has maybe proxied
/// @param proxyType The permissions allowed for the proxy
/// @param delay The announcement period required of the initial proxy, will generally be zero
/// @return exists True if a proxy exists, False otherwise
function isProxy(
address real,
address delegate,
ProxyType proxyType,
uint32 delay
) external view returns (bool exists);
}
The interface includes the necessary data structures along with the following functions:
addProxy(delegate, proxyType, delay) — registers a proxy account for the sender after a specified number of delay
blocks (generally zero). Will fail if a proxy for the caller already exists
delegate
address - the proxy addressproxyType
ProxyType - the delegation type that defines the specific functions the proxy will be granted permission to executedelay
uint32 - number of blocks to wait until the proxy is enabled
delegate
- 0x3f0Aef9Bd799F1291b80376aD57530D353ab0217proxyType
- "Any"delay
- 0
removeProxy(delegate, proxyType, delay) — removes a registered proxy for the sender
delegate
address - the proxy address to removeproxyType
ProxyType - the delegation type to removedelay
uint32 - number of blocks to wait until the removal is in effect
delegate
- 0x3f0Aef9Bd799F1291b80376aD57530D353ab0217proxyType
- "Any"delay
- 0
removeProxies() — removes all of the proxy accounts delegated to the sender
isProxy(real, delegate, proxyType, delay) — returns true
if the delegate address is a proxy of type proxyType
, for address real
, with the specified delay
real
address - the account granting permissions to the proxydelegate
address - the proxy addressproxyType
ProxyType - the delegation typedelay
uint32 - number of blocks to wait
delegate
- 0xbB8919d5DDfc85F4D15820a9e58018f1cfB39a2Fdelegate
- 0x3f0Aef9Bd799F1291b80376aD57530D353ab0217proxyType
- "Any"delay
- 0
The proxyType
parameter is defined by the following ProxyType
enum, where the values start at 0
with the most permissive proxy type and are represented as uint8
values:
enum ProxyType {
Any,
NonTransfer,
Governance,
Staking,
CancelProxy,
Balances,
AuthorMapping,
IdentityJudgement
}
Proxy Types¶
There are multiple types of proxy roles that can be delegated to accounts, represented in Proxy.sol
through the ProxyType
enum. The following list includes all of the possible proxies and the type of transactions they can make on behalf of the primary account:
- Any — the any proxy will allow the proxy account to make any type of transaction. Note that balance transfers are only allowed to EOAs, not contracts or Precompiles
- NonTransfer — the non-transfer proxy allows the proxy account to make any type of transaction where the
msg.value
is checked to be zero - Governance - the governance proxy will allow the proxy account to make any type of governance related transaction
- CancelProxy - the cancel proxy will allow the proxy account to reject and remove delayed proxy announcements (of the primary account). Currently, this is not an action supported by the Proxy Precompile
- Balances - the balances proxy will allow the proxy account to only make balance transfers to EOAs
Note
The Solidity interface contains more proxy types than those listed above. The previous list includes only those proxy types implemented in the baseline EVM Template.
Interact with the Solidity Interface via Remix¶
This section will walk you through the steps to create a proxy, verify its creation, and revoke the proxy privileges. These examples are based on the Tanssi demo EVM Appchain and use Metamask. This guide can be adapted for your own Tanssi EVM appchain by adding the RPC URL of your Tanssi appchain to the EVM Wallet of your choosing.
Remix Set Up¶
You can interact with the Proxy precompile using Remix. To add the precompile to Remix, you will need to:
- Get a copy of
Proxy.sol
- Paste the file contents into a Remix file named
Proxy.sol
Compile the Contract¶
Next, you will need to compile the interface in Remix:
- Click on the Compile tab, second from top
- Then to compile the interface, click on Compile Proxy.sol
When compilation is completed, you will see a green checkmark next to the Compile tab.
Access the Contract¶
Instead of deploying the smart contract, you will access the interface through its address:
- Click on the Deploy and Run tab directly below the Compile tab in Remix
- Make sure Injected Provider - Metamask is selected in the ENVIRONMENT dropdown. You may be prompted by MetaMask to connect your account to Remix if it's not already connected
- Make sure the priimary account is displayed under ACCOUNT
- Ensure Proxy - Proxy.sol is selected in the CONTRACT dropdown. Given that it is a precompiled contract, there is no deployment step. Instead, you are going to provide the address of the precompile in the At Address field
- Provide the address of the Proxy precompile (which is
0x0000000000000000000000000000000000000805
in this example) and click At Address - The Proxy precompile will appear in the list of Deployed Contracts
Add a Proxy¶
You can add a proxies for your account calling the precompile functions. In the following example, you will add a proxy allowed to execute any transaction on your behalf:
- Expand the Proxy Precompile contract to see the available functions
- Find the addProxy function and press the button to expand the section
- Insert your second account's address as the delegate,
0
as proxyType, meaningany
, and0
as delay - Click transact
- MetaMask will pop up, and you will be prompted to review the transaction details. Click Confirm to execute the transaction
Note
When constructing the transaction in Remix, the proxyType is represented as a uint8
, instead of the expected enum ProxyType
. In Solidity, enums are compiled as uint8
, so when you pass in 0
for proxyType, you indicate the first element in the ProxyType
enum, which is the any
proxy.
Check a Proxy Existence¶
The function isProxy
checks if a proxy account exists. After creating a proxy in the previous step, use the same parameters to verify that the proxy was successfully added:
- Expand the isProxy function
- Insert your primary account as real, your second account (proxy) as delegate,
0
as proxyType, and0
as delay - Click call
- The functions returns whether there is a proxy or not. In this example, the proxy exists, hence the function returns
true
Remove a Proxy¶
You can revoke a proxy permission when it's no longer needed. After creating a proxy in the Add Proxy, step, it can be removed following these steps:
- Expand the removeProxy function
- Insert the proxy account as the delegate,
0
as proxyType,0
and as delay - Click transact
- MetaMask will pop up, and you will be prompted to review the transaction details. Click Confirm to execute the transaction
After the transaction is confirmed, if you repeat the steps to check for a proxy existence, the result should be false
.
And that's it! You've successfully interacted with the Proxy precompile using MetaMask and Remix!
| Created: September 30, 2024