Run Prividium Chain

Get started running a local Prividium chain with ZKsync Stack.

This guide shows you how to use the ZK Stack CLI to run local a Prividium chain. By following this guide you will:

  • Set up a local Elastic Network ecosystem
  • Create a Prividium chain
  • Deploy a smart contract to your chain
  • Test interacting with your contract
  • Run a local block explorer

Installing ZK Stack CLI

Before getting started, make sure to install all of the dependencies for the zksync-era repo by following the instructions in the matter-labs/zksync-era project's Setup dev guide.

You can use zkstackup to install and manage zkstack:

curl -L https://raw.githubusercontent.com/matter-labs/zksync-era/main/zkstack_cli/zkstackup/install | bash
zkstackup
You can find a full reference for the ZK Stack CLI in the zksync-era repo.

Setting up the Elastic Network ecosystem

The first step is to create a new ecosystem with the zkstack ecosystem create command.

Make sure Docker is running on your machine.

Move to a directory where you want your ecosystem folder to be, and run the command below to generate an ecosystem folder.

zkstack ecosystem create

You will be prompted with a series of options to customize your ecosystem and generate a new chain within the ecosystem. For this tutorial, use the options shown below. If you choose different names for your ecosystem or chain, remember to update the names in the commands later on.

Deploying the ecosystem

You've just set up your ecosystem and chain, and have two Docker containers running: a postgres database for your chain, and a reth node for the local L1 chain. Make sure to have the following ports free: 3050, 3070, 4041, 5432, and 8545.

The L1 chain is already running, but your ecosystem and chain aren't deployed yet. The next step is to deploy your ecosystem contracts to the L1 and register your chain to the ecosystem.

Move into the ecosystem folder:

cd my_elastic_network

Next, run the zkstack ecosystem init command below to deploy the ecosystem:

zkstack ecosystem init --dev --validium-type no-da
If you have any issues at this step, try reinstalling the dependencies at the top.

Starting the chain server

The last step here is to start a server for prividium_chain:

zkstack server

With this, your L1 chain should be running at port 8545, the prividium_chain database running at port 5432, and the prividium_chain node running at port 3050.

Note that the node running at port 3050 provides full access to your chain. This URL should be kept private.

Funding a wallet on your chain

Because you chose to use a local reth node for your L1 and selected ETH as the base asset, you have access to several rich wallets on the L1 that you can use to bridge ETH to prividium_chain.

You can find a full list of rich wallet addresses and their private keys in the ZKsync docs.

Never use these wallets in production or send real funds to them.

Open a new terminal and run the command below to bridge some ETH to prividium_chain using ZKsync CLI:

npx zksync-cli bridge deposit --rpc=http://localhost:3050 --l1-rpc=http://localhost:8545

For testing purposes, we'll use one of the rich wallets as both the sender and recipient:

? Amount to deposit 10
? Private key of the sender 0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110
? Recipient address on L2 0x36615Cf349d7F6344891B1e7CA7C72883F5dc049

To see that it worked, let's check the balance of that address on prividium_chain:

npx zksync-cli wallet balance \
--address 0x36615Cf349d7F6344891B1e7CA7C72883F5dc049 \
--rpc http://localhost:3050

Now this address has ETH available on prividium_chain to use for testing.

Deploying a contract

Now that your chain is deployed and your wallet is funded, let's create a template contract and deploy it to prividium_chain:

Move out of your ecosystem folder and initialize a new hardhat project using ZKsync CLI:

npx zksync-cli create prividium-token --template hardhat_solidity --project contracts
cd prividium-token

Use the same private key for the rich wallet:

? Private key of the wallet responsible for deploying contracts (optional)
0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110

In the hardhat.config.ts file, let's configure the local network as the default:

defaultNetwork: "dockerizedNode",

Note that this will connect hardhat to your standard full-access RPC API, which allows you to deploy contracts.

Finally, compile the contracts and run the deploy script for the ERC20 token contract:

npm run deploy-erc20

Save the deployed contract address for later.

Proxy RPC 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.

Configuring access

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.

ERC-20 Config

Replace the generated access config with the one below:

Then replace <0xYOUR_CONTRACT_ADDRESS> with the deployed ERC-20 contract address.

Note that the transfer and approve methods are marked as public, however this doesn't mean that anyone can see the details of these transactions. This is because for these methods, the private proxy RPC API validates that current user is equal to the msg.sender in the transactions.

To apply changes to the permissions file, you will need to restart the Proxy API. If your config file isn't correctly configured, the API won't start.

Interacting With the contract

Make a new file in the scripts folder called priv-interact.ts and copy/paste the script below:

This script:

  • Registers the deployer address as a user
  • Uses the generated user token to access the Proxy RPC API
  • Sends tokens from the deployer address to another address

There is no method available to fetch a token that was previously generated. If you generate a user token and it's forgotten, you can simply generate a new token.

Before running, update your .env file with the RECIPIENT_ADDRESS and CONTRACT_ADDRESS. Use the deployed ERC20 contract address for CONTRACT_ADDRESS, and any other wallet address for the recipient. Choose an address you also have the private key for.

Then, run the interact script with:

npx hardhat run ./scripts/priv-interact.ts

Note that the user will be able to see their own balance, but not the balance of the recipient wallet. If you try to access the balance from a different wallet (or without a wallet), you will see an Unauthorized error.

Checking balance

Make a new file called check-balance.ts in the scripts folder and copy/paste the script below.

This script just checks the ERC20 balance of the default wallet.

To check the balance of another address, you must register a user token for that address, update the private key in your .env file, and then run the check-balance script.

Before running, update your .env file with the USER_TOKEN obtained during the previous step.

You can run the script with:

npx hardhat run ./scripts/check-balance.ts

Block explorer

The next step is to set up the block explorer and contract verifier service.

Start the contract verifier

The contract verifier is used to check contract source code against deployed bytecode. This is going to be used in the explorer to display the source code and ABIs of contracts.

In a new terminal run:

zkstack contract-verifier init \
--zksolc-version v1.5.6 \
--zkvyper-version v1.5.10  \
--solc-version 0.8.24 \
--era-vm-solc-version 0.8.28-1.0.2 \
--vyper-version v0.4.1

This will download the needed binaries for verifying contracts on the block explorer.

Next, run:

zkstack contract-verifier run

Once this is done you are going to have the verifier running on port 3070.

Setting up a block explorer

In a new terminal run:

zkstack explorer init --prividium

You can select the default options in the prompts.

This command creates a database to store explorer data and generates a docker compose file with explorer services at prividium_chain/configs/explorer-docker-compose.yml.

Next, you need to start the explorer backend services:

zkstack explorer backend

This command uses the previously created docker compose file to start the services required for the explorer.

Finally, in a new terminal you can run the explorer app:

zkstack explorer run

This command will start the dockerized explorer app using configuration from apps/explorer.config.json file inside your ecosystem directory. You can edit this file to configure the app if needed.

You can now navigate to the explorer web-app. By default, the explorer frontend starts on http://127.0.0.1:3010, you can configure the port in apps.yaml file.

Switching to an internal explorer

To switch between the public-facing Prividium block explorer and a standard full-access block explorer for internal use:

  1. Stop the explorer frontend.
  2. Stop the explorer backend.
  3. Run zkstack explorer init and select No when prompted to use the Prividium explorer.
  4. Restart the explorer backend and frontend services.

Right now both versions of the explorer can't be run at the same time with zkstack, but it's possible to configure this manually.

To switch back to a Prividium block explorer, re-run the steps from "Setting up a block explorer".

Using the Block Explorer

To use the public-facing Prividium block explorer, users must first sign in with their wallet. The block explorer then provides the configured level of access based on their account.

Restarting your Chain

If at some point during development you wish to stop and restart your chain, here are the complete steps for restarting.

To restart the chain and RPC APIs, go to the ecosystem folder and:

  1. Run zkstack containers to restart the L1 and postgres Docker containers. If you have deleted the containers, go through the initial ecosystem setup steps again.
  2. Run zkstack server to start the standard RPC API.
  3. In a new terminal, start the proxy RPC API with zkstack private-rpc run.
  4. In a new terminal, start the contract verifier with zkstack contract-verifier run.
  5. In another terminal, start the explorer backend with zkstack explorer backend.
  6. In another terminal, start the explorer frontend with zkstack explorer run.

Next Steps

To see a full example application running on Prividium, check out this example escrow application.


Made with ❤️ by the ZKsync Community