Skip to main content

Custom testnets

Advanced
Tutorial

Overview

ICP does not have a public testnet network that developers can use to deploy and test canister smart contracts because canisters deployed to the mainnet can be upgraded and changed, plus deployment costs are fairly low compared to other chains.

To provide a testnet-like environment, ICP has a smart contract playground network that can be used for small-scale, temporary testing. Canisters deployed to the playground are restricted to certain parameters. These limitations are:

  • Cycle transfer instructions are silently ignored by the playground.
  • Canisters can use at most 1GiB of memory.
  • Canisters can call the management canister to manage itself without being the controller.
  • Deployed canisters expire after 20 minutes. Upgrades to canisters reset this timer. When the timer runs out, the canister(s) will be uninstalled.
  • Wasm files can't be gzipped.
  • Wasm files will be analyzed to remove any potentially expensive or malicious operations.

For advanced developers and use cases that want to use a testnet environment without these restrictions, there are two options:

  • Private testnets: Deploy your own instance of the playground, allowing you to control the playground settings, remove restrictions, and modify the canister pool. This option costs cycles, as you will need to fund your instance of the playground with cycles that will be used to fund canisters deployed to your playground.

  • Synthetic testnets: Use the dfx named network feature to create a local network that is segmented away from the default local network created by dfx.

Private testnet

To create a private testnet, you can create a custom instance of the playground. Using your own instance of the playground allows for extensive customization, such as:

  • Enabling access control by restricting the playground's usage to only allow certain principals.

  • Configuring more generous canister timeouts and the number of available cycles.

  • Allowing some or all of the functions that the public playground does not allow, such as sending cycles to other canisters.

Using a custom playground can also help simplify development for teams, since the whole team can use a custom playground without needing to manage individual cycle balances.

Creating a custom playground instance

  • Step 1: Clone the playground repo with the command:

git clone https://github.com/dfinity/motoko-playground

Some settings you may want to change are:

let wasm = args.wasm_module;
  • Step 4: Then deploy the pool canister, and if necessary, deploy the wasm-utils canister:

dfx deploy pool
dfx deploy wasm-utils
  • Step 5: Lastly, define the local playground network in your project's dfx.json file.

In this definition, you will need to set the playground canister's ID (the pool canister ID) and define the amount of seconds before a canister is returned to the pool, as shown below:

dfx.json
"<network name>": {
  "playground": {
    "playground_canister": "<canister pool id>",
    "timeout_seconds": <amount of seconds after which a canister is returned to the pool>
  },
  "providers": [
      "https://icp0.io"
  ]
}

If the value <network name> is set as playground, then the command dfx deploy --playground will deploy to your custom playground. Otherwise, the command has to use --network <network name>.

This network definition can also go into the networks.json file, so it applies to every project, not just the current project since dfx.json files are project-specific. To see where the networks.json file is located, use the command dfx info networks-json-path.

Synthetic testnet

Another option to create a custom testnet is to use the dfx named network functionality. You can create a custom named network for each project, therefore creating a synthetic testnet that is segmented from the other locally tested projects in your environment. Local deployments mirror the mainnet as closely as possible, but local deployments only run a single subnet. Using a custom local network allows you to test integrations with services and estimate deployment costs.

In a custom network, it is possible to run any dfx command that would otherwise take --network ic but using --network myNetwork instead. myNetwork can be replaced with any other name, except the three reserved ones: ic, local, and playground.

Networks are defined in two ways: assumed and explicitly configured. dfx only contains the ic network as an assumed environment. All other networks are explicitly configured in the networks.json or dfx.json files. The "networks" section of dfx.json should contain at least the local network, which gets chosen by default if no other network is specified with the --network flag.

System-wide networks

Custom networks can be configured in the networks.json configuration file for dfx, which can be found using the following dfx command:

dfx info networks-json-path

This will return the file path where your system's networks.json file is located. Networks defined in this file can be used by any project in your local environment.

  • Step 1: To define a custom network, you can edit the networks.json file to define a binding address such as localhost or any other domain name.

For example:

networks.json
{
  "myNetwork1": {
    "bind": "localhost:4943",
    "replica": {
      "subnet_type": "application"
    }
  }
}

To define multiple networks, use multiple definitions on different domain names:

networks.json
{
  "myNetwork1": {
    "bind": "localhost:4943",
    "replica": {
      "subnet_type": "application"
    }
  },
  "myNetwork2": {
    "bind": "127.0.0.1:4943",
    "replica": {
      "subnet_type": "application"
    }
  }
}
  • Step 2: Then, to deploy to one of these networks, use the --network flag with dfx deploy:

dfx deploy --network myNetwork2

Project specific networks

Alternatively, networks can also be defined in a project's dfx.json file.

Only canisters defined in that dfx.json file can be deployed to that network.

To define a project-specific network, add a "networks" section to your dfx.json file, such as:

dfx.json
"networks": {
    "myNetwork": {
      "providers": [
        "https://ic0.app"
      ],
      "type": "persistent"
    }
  }

This definition uses the following parameters:

  • providers: The network provider; can be localhost or any other domain name. Domain names must be a full URL, such as https://domain.com.
  • type: The type of network, either ephemeral or persistent. Ephemeral networks do not retain the same IDs for canisters, while persistent networks will retain the same canister IDs.

Configuring a wallet

If you are using a cycles wallet, the cycles wallet for each network is stored separately.

Please note that the cycles wallet will be removed from dfx in a future release.

It is recommended to use the cycles ledger instead.

To use the same cycles wallet as on the main ic network, first make sure the correct identity is set by running the command:

dfx identity use <identity name>

Then, read the ic network's currently configured wallet using:

dfx identity get-wallet --network ic

Set the wallet for the newly defined network with the command:

dfx identity set-wallet <wallet id> --network myNetwork

These commands can be used together, such as:

dfx identity set-wallet "$(dfx identity get-wallet --network ic)" --network myNetwork

If you prefer to use a separate cycles wallet for the staging environment, follow the instructions in the step 'Creating a cycles wallet' in the deploying to the mainnet guide.

Resources