Skip to content

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 address
  • proxyType ProxyType - the delegation type that defines the specific functions the proxy will be granted permission to execute
  • delay uint32 - number of blocks to wait until the proxy is enabled
  • delegate - 0x3f0Aef9Bd799F1291b80376aD57530D353ab0217
  • proxyType - "Any"
  • delay - 0
removeProxy(delegate, proxyType, delay) — removes a registered proxy for the sender
  • delegate address - the proxy address to remove
  • proxyType ProxyType - the delegation type to remove
  • delay uint32 - number of blocks to wait until the removal is in effect
  • delegate - 0x3f0Aef9Bd799F1291b80376aD57530D353ab0217
  • proxyType - "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 proxy
  • delegate address - the proxy address
  • proxyType ProxyType - the delegation type
  • delay uint32 - number of blocks to wait
  • delegate - 0xbB8919d5DDfc85F4D15820a9e58018f1cfB39a2F
  • delegate - 0x3f0Aef9Bd799F1291b80376aD57530D353ab0217
  • proxyType - "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:

  1. Get a copy of Proxy.sol
  2. Paste the file contents into a Remix file named Proxy.sol

Compile the Contract

Next, you will need to compile the interface in Remix:

  1. Click on the Compile tab, second from top
  2. Then to compile the interface, click on Compile Proxy.sol

Compiling 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:

  1. Click on the Deploy and Run tab directly below the Compile tab in Remix
  2. 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
  3. Make sure the priimary account is displayed under ACCOUNT
  4. 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
  5. Provide the address of the Proxy precompile (which is 0x0000000000000000000000000000000000000805 in this example) and click At Address
  6. The Proxy precompile will appear in the list of Deployed Contracts

Access the address

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:

  1. Expand the Proxy Precompile contract to see the available functions
  2. Find the addProxy function and press the button to expand the section
  3. Insert your second account's address as the delegate, 0 as proxyType, meaning any, and 0 as delay
  4. Click transact
  5. 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.

Call the addProxy function

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:

  1. Expand the isProxy function
  2. Insert your primary account as real, your second account (proxy) as delegate, 0 as proxyType, and 0 as delay
  3. Click call
  4. The functions returns whether there is a proxy or not. In this example, the proxy exists, hence the function returns true

Call the isProxy function

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:

  1. Expand the removeProxy function
  2. Insert the proxy account as the delegate, 0 as proxyType, 0 and as delay
  3. Click transact
  4. 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.

Call the removeProxy function

And that's it! You've successfully interacted with the Proxy precompile using MetaMask and Remix!

The information presented herein has been provided by third parties and is made available solely for general information purposes. Tanssi does not endorse any project listed and described on the Tanssi Doc Website (https://docs.tanssi.network/). Tanssi Foundation does not warrant the accuracy, completeness or usefulness of this information. Any reliance you place on such information is strictly at your own risk. Tanssi Foundation disclaims all liability and responsibility arising from any reliance placed on this information by you or by anyone who may be informed of any of its contents. All statements and/or opinions expressed in these materials are solely the responsibility of the person or entity providing those materials and do not necessarily represent the opinion of Tanssi Foundation. The information should not be construed as professional or financial advice of any kind. Advice from a suitably qualified professional should always be sought in relation to any particular matter or circumstance. The information herein may link to or integrate with other websites operated or content provided by third parties, and such other websites may link to this website. Tanssi Foundation has no control over any such other websites or their content and will have no liability arising out of or related to such websites or their content. The existence of any such link does not constitute an endorsement of such websites, the content of the websites, or the operators of the websites. These links are being provided to you only as a convenience and you release and hold Tanssi Foundation harmless from any and all liability arising from your use of this information or the information provided by any third-party website or service.
Last update: October 1, 2024
| Created: September 30, 2024