Proxy RPC API

Learn about the Prividium Proxy RPC API.

The proxy RPC API is the main component that enables privacy in the ZKsync Stack.

In a standard rollup, there is a standard RPC API that provides full access to transaction data for users. The proxy RPC is an additional layer in between end users and the chain's standard RPC API. The proxy layer enables authentication of requests to the standard RPC.

Access to the standard RPC should remain private. We highly recommend implementing a secure firewall around the standard RPC API. Only the proxy RPC API endpoint should be publicly shared.

Setting up the Proxy

After your ecosystem and validium chain are setup, you can initialize the Proxy API.

To initialize the Proxy API service, open a new terminal in your ecosystem folder and run:

zkstack private-rpc init

You can select the default options for the prompts.

If you get an error at this step, you may need to allow Docker to use more resources on your system.

This command will generate two files:

  • A private proxy docker-compose file at /chains/prividium_chain/configs/private-proxy-docker-compose.yml
  • An example permissions config file at /chains/prividium_chain/configs/private-rpc-permissions.yaml.

Next, you can start the Proxy API service with:

zkstack private-rpc run

The Proxy API should now be running at port 4041.

Upgrading the Proxy

To upgrade your proxy API to the latest version:

  1. Run zkstackup to upgrade your version of zkstack.
  2. Update the zksync-era repo in the ecosystem folder.
  3. Delete the private-rpc Docker container.
  4. Re-run the zkstack private-rpc init command. This command will update the chain's private-proxy-docker-compose.yml file, but won't change the private-rpc-permissions.yaml config.
  5. Start the server with zkstack private-rpc run.

Configuring the Proxy

In the generated docker compose file, you can configure the environment variables as needed.

The service is configured through six key environment variables:

  • CORS_ORIGIN: sets the web-origin domain that the API will accept requests from. It defaults to the local port where the block explorer runs. It can also be an array of domains separated by a comma.
  • CREATE_TOKEN_SECRET: the shared secret used to verify clients requesting to create new access tokens.
  • PERMISSIONS_YAML_PATH: points to the YAML file that lists the access rules enforced by the proxy.
  • DATABASE_URL: points to the chain's PostgreSQL database.
  • PORT: the port where the proxy itself listens (4041 by default).
  • TARGET_RPC: the standard internal RPC API endpoint the proxy forwards validated requests to.

Configuring Access

To configure access to different smart contracts on your chain, Prividium uses a simple YAML file. Edits to the permissions file only require a restart of the Proxy service to be applied.

You can see what an example configuration file looks like below:

groups:
  - name: 'group1'
    members:
      - '0xeaAFbF6Fc352B0598e34f4F282939720D9cf0f59'
  - name: 'group2'
    members:
      - '0x71e6dDfE9074786Fd8e986C53f78D25450d614D5'
contracts:
  - address: '0xBE06E7e23AA92a6B0523A0E7cBb43690De7af8DB'
    methods:
      - signature: 'function number() (uint256)'
        read:
          type: 'public'
        write:
          type: 'public'
      - signature: 'function owner() (address)'
        read:
          type: 'group'
          groups: ['group1']
        write:
          type: 'group'
          groups: ['group1']
      - signature: 'function fromGroup1Address() public'
        read:
          type: 'group'
          groups: ['group1']
        write:
          type: 'group'
          groups: ['group1']
      - signature: 'function fromGroup2Address() public'
        read:
          type: 'group'
          groups: ['group2']
        write:
          type: 'group'
          groups: ['group2']
      - signature: 'function hiTo(address) public'
        read:
          type: 'checkArgument'
          argIndex: 0
        write:
          type: 'checkArgument'
          argIndex: 0

The permissions config file at /chains/prividium_chain/configs/private-rpc-permissions.yaml can be edited based on what access you want end users to have.

There are two sections in the permissions file:

  • groups: Logical collections of users or addresses sharing the same permissions.
  • contracts: Specific contracts or methods that a group is allowed to access.

Groups Access

You can define hard-coded groups of administrative addresses in the groups section. A group consists of a name and a list of member addresses.

After defining a group, the group name can be used in the contracts section to grant the entire group certain access.

Contracts Access

The contracts section defines the level of access available to specific contracts and their functions. If a contract is not included here, or a function not defined, it can only be accessed via the standard RPC API.

Note that contracts can only be deployed via the standard RPC API.

Defining Methods

For a given contract address, the methods field defines the contract functions that can be called.

The function signature is used to identify functions and define rules for them. The format should the same signature as the canonical signature defined in Solidity ABI.

To generate a list of your contract's functions in this format, you can use the formatAbiItem method from abitype, as shown in the example script below.

import ABI_JSON from '../artifacts-zk/contracts/erc20/MyERC20Token.sol/MyERC20Token.json';
import { type AbiFunction, formatAbiItem } from 'abitype';

async function main() {
  const { abi } = ABI_JSON;

  abi.forEach((item) => {
    if (item.type === 'function') {
      const signature = formatAbiItem(item as AbiFunction);
      console.log(signature);
    }
  });
}

Method Access

For each function, you can define a rule for read and write regardless of whether the function itself is a pure function.

For each rule within a read or write section, you must choose a type.

The types available out-of-the-box include:

  • public: anyone can call this function.
  • closed: no one can call this function (default).
  • group: only the specified groups can call this.
  • checkArgument: the specified argument index must match the function caller's address.
  • oneOf: allows you to define more than one rule type. It functions as an OR operator; if at least one condition is met, access is granted.

In addition to these rules, there is a universal rule applied so that users can only see transactions where their address is equal to the msg.sender.

You can fully customize the types and their access logic by editing the zksync-era/private-rpc/src/permissions/yaml-parser.ts file.

The example implementation showcases one approach, but it can be easily modified to accommodate unique workflows or compliance requirements.


Made with ❤️ by the ZKsync Community